packages feed

syncthing-hs 0.1.2.0 → 0.2.0.0

raw patch · 26 files changed

+701/−396 lines, 26 filesdep +data-defaultdep +http-typesdep +scientificPVP ok

version bump matches the API change (PVP)

Dependencies added: data-default, http-types, scientific, vector

API changes (from Hackage documentation)

- Network.Syncthing.Get: model :: MonadSync m => FolderName -> SyncM m Model
- Network.Syncthing.Get: sync :: MonadSync m => SyncM m Bool
- Network.Syncthing.Get: system :: MonadSync m => SyncM m System
- Network.Syncthing.Get: tree :: MonadSync m => FolderName -> Maybe Path -> Maybe Int -> SyncM m (Maybe DirTree)
- Network.Syncthing.Post: bump :: MonadSync m => FolderName -> Path -> SyncM m Need
- Network.Syncthing.Post: hint :: MonadSync m => Device -> Server -> SyncM m ()
- Network.Syncthing.Post: scanFolder :: MonadSync m => FolderName -> Maybe Path -> SyncM m ()
- Network.Syncthing.Post: sendConfig :: MonadSync m => Config -> SyncM m ()
- Network.Syncthing.Post: sendIgnores :: MonadSync m => FolderName -> [Text] -> SyncM m (Maybe [Text])
- Network.Syncthing.Types: Progress :: Text -> Int -> Integer -> Int -> Int -> Int -> Integer -> Progress
- Network.Syncthing.Types: data Progress
- Network.Syncthing.Types: getGlobalAnnEnabled :: OptionsConfig -> Bool
- Network.Syncthing.Types: getGlobalAnnServers :: OptionsConfig -> [Text]
- Network.Syncthing.Types: getLocalAnnEnabled :: OptionsConfig -> Bool
- Network.Syncthing.Types: getLocalAnnMCAddr :: OptionsConfig -> Text
- Network.Syncthing.Types: getLocalAnnPort :: OptionsConfig -> Int
- Network.Syncthing.Types: getProgressVersion :: Progress -> Int
- Network.Syncthing.Types: getReconnectIntervalS :: OptionsConfig -> Int
- Network.Syncthing.Types: getUPnPEnabled :: OptionsConfig -> Bool
- Network.Syncthing.Types: getUPnPLease :: OptionsConfig -> Int
- Network.Syncthing.Types: getUPnPRenewal :: OptionsConfig -> Int
- Network.Syncthing.Types: getURAccepted :: OptionsConfig -> Int
- Network.Syncthing.Types: getURUniqueID :: OptionsConfig -> Text
+ Network.Syncthing.Get: browse :: MonadSync m => FolderName -> Maybe Path -> Maybe Int -> SyncM m (Maybe DirTree)
+ Network.Syncthing.Get: dbStatus :: MonadSync m => FolderName -> SyncM m Model
+ Network.Syncthing.Get: devices :: MonadSync m => SyncM m (Map Device DeviceInfo)
+ Network.Syncthing.Get: file :: MonadSync m => FolderName -> Path -> SyncM m DBFile
+ Network.Syncthing.Get: folders :: MonadSync m => SyncM m (Map FolderName FolderInfo)
+ Network.Syncthing.Get: insync :: MonadSync m => SyncM m Bool
+ Network.Syncthing.Get: lang :: MonadSync m => SyncM m [Text]
+ Network.Syncthing.Get: sysStatus :: MonadSync m => SyncM m System
+ Network.Syncthing.Internal: runSyncM :: SyncConfig -> SyncM m a -> m (SyncResult a)
+ Network.Syncthing.Post: config :: MonadSync m => Config -> SyncM m ()
+ Network.Syncthing.Post: discovery :: MonadSync m => Device -> Server -> SyncM m ()
+ Network.Syncthing.Post: ignores :: MonadSync m => FolderName -> [Text] -> SyncM m (Maybe [Text])
+ Network.Syncthing.Post: prio :: MonadSync m => FolderName -> Path -> SyncM m Need
+ Network.Syncthing.Post: scan :: MonadSync m => FolderName -> Maybe Path -> SyncM m ()
+ Network.Syncthing.Types: Connections :: Map Device Connection -> Connection -> Connections
+ Network.Syncthing.Types: DBFile :: [Device] -> FileInfo -> FileInfo -> DBFile
+ Network.Syncthing.Types: DeviceInfo :: Maybe UTCTime -> DeviceInfo
+ Network.Syncthing.Types: FileInfo :: Text -> Text -> Maybe UTCTime -> [Text] -> Int -> Integer -> Maybe Int -> FileInfo
+ Network.Syncthing.Types: FolderInfo :: LastFile -> FolderInfo
+ Network.Syncthing.Types: LastFile :: Text -> Maybe UTCTime -> LastFile
+ Network.Syncthing.Types: data Connections
+ Network.Syncthing.Types: data DBFile
+ Network.Syncthing.Types: data DeviceInfo
+ Network.Syncthing.Types: data FileInfo
+ Network.Syncthing.Types: data FolderInfo
+ Network.Syncthing.Types: data LastFile
+ Network.Syncthing.Types: getAutoNormalize :: FolderConfig -> Bool
+ Network.Syncthing.Types: getAvailability :: DBFile -> [Device]
+ Network.Syncthing.Types: getConnections :: Connections -> Map Device Connection
+ Network.Syncthing.Types: getFileName :: LastFile -> Text
+ Network.Syncthing.Types: getFileVersion :: FileInfo -> [Text]
+ Network.Syncthing.Types: getGlobal :: DBFile -> FileInfo
+ Network.Syncthing.Types: getGlobalAnnounceEnabled :: OptionsConfig -> Bool
+ Network.Syncthing.Types: getGlobalAnnounceServers :: OptionsConfig -> [Text]
+ Network.Syncthing.Types: getHashers :: FolderConfig -> Int
+ Network.Syncthing.Types: getLastFile :: FolderInfo -> LastFile
+ Network.Syncthing.Types: getLastSeen :: DeviceInfo -> Maybe UTCTime
+ Network.Syncthing.Types: getLimitBandwidthInLan :: OptionsConfig -> Bool
+ Network.Syncthing.Types: getLocal :: DBFile -> FileInfo
+ Network.Syncthing.Types: getLocalAnnounceEnabled :: OptionsConfig -> Bool
+ Network.Syncthing.Types: getLocalAnnounceMCAddr :: OptionsConfig -> Text
+ Network.Syncthing.Types: getLocalAnnouncePort :: OptionsConfig -> Int
+ Network.Syncthing.Types: getOrder :: FolderConfig -> Text
+ Network.Syncthing.Types: getPathSeparator :: System -> Text
+ Network.Syncthing.Types: getReconnectionIntervalS :: OptionsConfig -> Int
+ Network.Syncthing.Types: getSyncedAt :: LastFile -> Maybe UTCTime
+ Network.Syncthing.Types: getTilde :: System -> Text
+ Network.Syncthing.Types: getTotal :: Connections -> Connection
+ Network.Syncthing.Types: getUpnpEnabled :: OptionsConfig -> Bool
+ Network.Syncthing.Types: getUpnpLeaseMinutes :: OptionsConfig -> Int
+ Network.Syncthing.Types: getUpnpRenewalMinutes :: OptionsConfig -> Int
+ Network.Syncthing.Types: getUpnpTimeoutSeconds :: OptionsConfig -> Int
+ Network.Syncthing.Types: getUptime :: System -> Integer
+ Network.Syncthing.Types: getUrAccepted :: OptionsConfig -> Int
+ Network.Syncthing.Types: getUrUniqueID :: OptionsConfig -> Text
- Network.Syncthing.Get: connections :: MonadSync m => SyncM m (Map Device Connection)
+ Network.Syncthing.Get: connections :: MonadSync m => SyncM m Connections
- Network.Syncthing.Types: DeviceConfig :: Device -> Text -> [AddressType] -> Bool -> Text -> Bool -> DeviceConfig
+ Network.Syncthing.Types: DeviceConfig :: Device -> Text -> [AddressType] -> Text -> Text -> Bool -> DeviceConfig
- Network.Syncthing.Types: File :: Integer -> Integer -> DirTree
+ Network.Syncthing.Types: File :: Maybe UTCTime -> Integer -> DirTree
- Network.Syncthing.Types: FolderConfig :: FolderName -> Path -> [Device] -> Bool -> Int -> Bool -> VersioningConfig -> Bool -> Int -> Int -> Text -> FolderConfig
+ Network.Syncthing.Types: FolderConfig :: FolderName -> Path -> [Device] -> Bool -> Int -> Bool -> Bool -> VersioningConfig -> Bool -> Int -> Int -> Int -> Text -> Text -> FolderConfig
- Network.Syncthing.Types: Need :: [Progress] -> [Text] -> [Text] -> Need
+ Network.Syncthing.Types: Need :: [FileInfo] -> [FileInfo] -> [FileInfo] -> Need
- Network.Syncthing.Types: OptionsConfig :: [Addr] -> [Text] -> Bool -> Bool -> Int -> Text -> Int -> Int -> Int -> Bool -> Bool -> Int -> Int -> Int -> Text -> Bool -> Int -> Int -> Bool -> Int -> Bool -> OptionsConfig
+ Network.Syncthing.Types: OptionsConfig :: [Addr] -> [Text] -> Bool -> Bool -> Int -> Text -> Int -> Int -> Int -> Bool -> Bool -> Int -> Int -> Int -> Int -> Text -> Bool -> Int -> Int -> Bool -> Int -> Bool -> Bool -> OptionsConfig
- Network.Syncthing.Types: System :: Integer -> Double -> Maybe (Map Server Bool) -> Int -> Text -> Integer -> System
+ Network.Syncthing.Types: System :: Integer -> Double -> Maybe (Map Server Bool) -> Int -> Text -> Integer -> Text -> Text -> Integer -> System
- Network.Syncthing.Types: getCompression :: DeviceConfig -> Bool
+ Network.Syncthing.Types: getCompression :: DeviceConfig -> Text
- Network.Syncthing.Types: getFlags :: Progress -> Int
+ Network.Syncthing.Types: getFlags :: FileInfo -> Text
- Network.Syncthing.Types: getLocalVersion :: Progress -> Int
+ Network.Syncthing.Types: getLocalVersion :: FileInfo -> Int
- Network.Syncthing.Types: getModTime :: DirTree -> Integer
+ Network.Syncthing.Types: getModTime :: DirTree -> Maybe UTCTime
- Network.Syncthing.Types: getModified :: Progress -> Integer
+ Network.Syncthing.Types: getModified :: FileInfo -> Maybe UTCTime
- Network.Syncthing.Types: getName :: Progress -> Text
+ Network.Syncthing.Types: getName :: FileInfo -> Text
- Network.Syncthing.Types: getNumBlocks :: Progress -> Int
+ Network.Syncthing.Types: getNumBlocks :: FileInfo -> Maybe Int
- Network.Syncthing.Types: getProgress :: Need -> [Progress]
+ Network.Syncthing.Types: getProgress :: Need -> [FileInfo]
- Network.Syncthing.Types: getQueued :: Need -> [Text]
+ Network.Syncthing.Types: getQueued :: Need -> [FileInfo]
- Network.Syncthing.Types: getRest :: Need -> [Text]
+ Network.Syncthing.Types: getRest :: Need -> [FileInfo]
- Network.Syncthing.Types: getSize :: Progress -> Integer
+ Network.Syncthing.Types: getSize :: FileInfo -> Integer

Files

Network/Syncthing.hs view
@@ -22,9 +22,9 @@ -- @ -- \{\-\# LANGUAGE OverloadedStrings \#\-\} ----- import qualified "Network.Wreq" as Wreq--- import "Control.Monad" ('Control.Monad.liftM2') -- import "Control.Lens" (('Control.Lens.&'), ('Control.Lens..~'), ('Control.Lens.?~'))+-- import "Control.Monad" ('Control.Monad.liftM2')+-- import qualified "Network.Wreq" as Wreq -- import "Network.Syncthing" -- import qualified "Network.Syncthing.Get" as Get --@@ -164,7 +164,7 @@ -- /Example:/ -- -- >>> defaultConfig--- SyncConfig { server = "127.0.0.1:8080", apiKey = Nothing, auth = Nothing, https = False, manager = Left _ }+-- SyncConfig { server = "127.0.0.1:8384", apiKey = Nothing, auth = Nothing, https = False, manager = Left _ } -- -- >>> defaultConfig { server = "192.168.0.10:8080", apiKey = Just "XXXX" } -- SyncConfig { server = "192.168.0.10:8080", apiKey = Just "XXXX", auth = Nothing, https = False, manager = Left _ }@@ -173,7 +173,7 @@ -- SyncConfig { server = "192.168.0.10:8080", apiKey = Just "XXXX", auth = Nothing, https = False, manager = Left _ } defaultConfig :: SyncConfig defaultConfig = SyncConfig {-      server   = "127.0.0.1:8080"+      server   = "127.0.0.1:8384"     , apiKey   = Nothing     , auth     = Nothing     , https    = False
Network/Syncthing/Get.hs view
@@ -16,24 +16,34 @@  module Network.Syncthing.Get     (-    -- * Request functions+    -- * System Services       ping     , apiKey     , config-    , completion+    , insync     , connections-    , deviceId     , discovery     , errors+    , sysStatus+    , upgrade+    , version++    -- * Database Services+    , browse+    , completion+    , file     , ignores-    , model     , need+    , dbStatus++    -- * Statistics Services+    , devices+    , folders++    -- * Miscellaneous Services+    , deviceId+    , lang     , report-    , sync-    , system-    , tree-    , upgrade-    , version     ) where  import           Control.Applicative                ((<$>))@@ -50,13 +60,11 @@  -- | Ping the Syncthing server. Returns the string \"pong\". ping :: MonadSync m => SyncM m Text-ping = getPing <$> ping'-  where-    ping' = query $ getRequest { path = "/rest/ping" }+ping = getPing <$> query getRequest { path = "/rest/system/ping" }  -- | Return the current configuration. config :: MonadSync m => SyncM m Config-config = query $ getRequest { path = "/rest/config" }+config = query getRequest { path = "/rest/system/config" }  -- | Get the API Key if available. apiKey :: MonadSync m => SyncM m (Maybe Text)@@ -65,90 +73,106 @@ -- | Return the completion percentage (0 to 100) for a given device and -- folder. completion :: MonadSync m => Device -> FolderName -> SyncM m Int-completion device folder = getCompletion <$> completion'-  where-    completion' = query $ getRequest { path   = "/rest/completion"-                                     , params = [ ("device", device)-                                                , ("folder", folder) ]-                                     }+completion device folder = +    getCompletion <$> query getRequest { path   = "/rest/db/completion"+                                       , params = [ ("device", device)+                                                  , ("folder", folder) ]+                                       }  -- | Get the list of current connections and some metadata associated -- with the connection/peer.-connections :: MonadSync m => SyncM m (M.Map Device Connection)-connections = query $ getRequest { path = "/rest/connections" }+connections :: MonadSync m => SyncM m Connections+connections = query getRequest { path = "/rest/system/connections" } +-- | Returns most data available about a given file, including version and+-- availability.+file :: MonadSync m => FolderName -> Path -> SyncM m DBFile+file folder filename = query getRequest { path   = "/rest/db/file"+                                        , params = [ ("folder", folder)+                                                   , ("file", filename) ]+                                        }+ -- | Verifiy and format a device ID. Return either a valid device ID in -- modern format, or an error. deviceId :: MonadSync m => Device -> SyncM m Device deviceId = deviceId' >=> either (liftLeft . InvalidDeviceId) liftRight   where     deviceId' :: MonadSync m => Device -> SyncM m (Either DeviceError Device)-    deviceId' device = query $ getRequest { path   = "/rest/deviceid"-                                          , params = [("id", device)]-                                          }+    deviceId' device = query getRequest { path   = "/rest/svc/deviceid"+                                        , params = [("id", device)]+                                        } +-- | Returns general statistics about devices.+devices :: MonadSync m => SyncM m (M.Map Device DeviceInfo)+devices = query getRequest { path = "/rest/stats/device" }+ -- | Fetch the contents of the local discovery cache. discovery :: MonadSync m => SyncM m (M.Map Device [CacheEntry])-discovery = query $ getRequest { path = "/rest/discovery" }+discovery = query getRequest { path = "/rest/system/discovery" }  -- | Get the list of recent errors. errors :: MonadSync m => SyncM m [Error]-errors = getErrors <$> errors'-  where-    errors' = query $ getRequest { path = "/rest/errors" }+errors = getErrors <$> query getRequest { path = "/rest/system/error" }  -- | Fetch the ignores list. ignores :: MonadSync m => FolderName -> SyncM m Ignore-ignores folder = query $ getRequest { path   = "/rest/ignores"-                                    , params = [("folder", folder)]-                                    }---- | Get information about the current status of a folder.-model :: MonadSync m => FolderName -> SyncM m Model-model folder = query $ getRequest { path   = "/rest/model"+ignores folder = query getRequest { path   = "/rest/db/ignores"                                   , params = [("folder", folder)]                                   } +-- | Get information about the current status of a folder.+dbStatus :: MonadSync m => FolderName -> SyncM m Model+dbStatus folder = query getRequest { path   = "/rest/db/status"+                                   , params = [("folder", folder)]+                                   }+ -- | Get lists of files which are needed by this device in order for it -- to become in sync. need :: MonadSync m => FolderName -> SyncM m Need-need folder = query $ getRequest { path   = "/rest/need"-                                 , params = [("folder", folder)]-                                 }+need folder = query getRequest { path   = "/rest/db/need"+                               , params = [("folder", folder)]+                               }  -- | Returns the data sent in the anonymous usage report. report :: MonadSync m => SyncM m UsageReport-report = query $ getRequest { path   = "/rest/report" }+report = query getRequest { path   = "/rest/svc/report" }  -- | Determine whether the config is in sync.-sync :: MonadSync m => SyncM m Bool-sync = getSync <$> sync'-  where-    sync' = query $ getRequest { path = "/rest/config/sync" }+insync :: MonadSync m => SyncM m Bool+insync = getSync <$> query getRequest { path = "/rest/system/config/insync" }  -- | Returns information about current system status and resource usage.-system :: MonadSync m => SyncM m System-system = query $ getRequest { path = "/rest/system" }+sysStatus :: MonadSync m => SyncM m System+sysStatus = query getRequest { path = "/rest/system/status" }  -- | Get the directory tree of the global model.-tree :: MonadSync m +browse :: MonadSync m      => FolderName -- ^ root folder     -> Maybe Path -- ^ defines a prefix within the tree where to start building the structure     -> Maybe Int  -- ^ defines how deep within the tree we want to dwell down (0 based, defaults to unlimited depth)       -> SyncM m (Maybe DirTree)-tree folder prefix levels  = -    queryMaybe $ getRequest { path   = "/rest/tree"-                            , params = [("folder", folder)] ++ optionals-                            }+browse folder prefix levels  = +    queryMaybe getRequest { path   = "/rest/db/browse"+                          , params = [("folder", folder)] ++ optionals+                          }   where      optionals  = catMaybes [("prefix",) <$> prefix, ("levels",) <$> levelsText]     levelsText = pack . show <$> levels  -- | Check for a possible upgrade. upgrade :: MonadSync m => SyncM m Upgrade-upgrade = query $ getRequest { path   = "/rest/upgrade" }+upgrade = query getRequest { path   = "/rest/system/upgrade" }  -- | Get the current syncthing version information. version :: MonadSync m => SyncM m Version-version = query $ getRequest { path = "/rest/version" }+version = query getRequest { path = "/rest/system/version" }++-- | Returns a list of canonicalized localization codes, as picked up from+-- the Accept-Language header sent by the browser.+lang :: MonadSync m => SyncM m [Text]+lang = query getRequest { path = "/rest/svc/lang" }++-- | Returns general statistics about folders. +folders :: MonadSync m => SyncM m (M.Map FolderName FolderInfo)+folders = query getRequest { path = "/rest/stats/folder" } 
Network/Syncthing/Internal/Monad.hs view
@@ -6,6 +6,7 @@     ( SyncResult     , SyncM(..)     , MonadSync(..)+    , runSyncM     , syncthingM     , liftEither     , liftReader@@ -45,10 +46,15 @@     getMethod  o s   = (^. W.responseBody) <$> W.getWith  o s     postMethod o s p = (^. W.responseBody) <$> W.postWith o s p --- | Run Syncthing requests.+-- | Run Syncthing requests without error handling.+runSyncM :: SyncConfig -> SyncM m a -> m (SyncResult a)+runSyncM config action =+    runReaderT (runEitherT $ runSyncthing action) config ++-- | Run Syncthing requests with error handling. syncthingM :: MonadCatch m => SyncConfig -> SyncM m a -> m (SyncResult a) syncthingM config action =-    runReaderT (runEitherT $ runSyncthing action) config `catch` syncErrHandler+    runSyncM config action `catch` syncErrHandler  liftEither :: Monad m => EitherT SyncError (ReaderT SyncConfig m) a -> SyncM m a liftEither = SyncM
Network/Syncthing/Internal/Request.hs view
@@ -88,14 +88,14 @@  getRequest :: SyncRequest getRequest = SyncRequest {-      path   = "/rest/ping"+      path   = "/rest/system/ping"     , method = get     , params = []     }  postRequest :: SyncRequest postRequest = SyncRequest {-      path   = "/rest/ping"+      path   = "/rest/system/ping"     , method = post ()     , params = []     }
Network/Syncthing/Internal/Types.hs view
@@ -12,7 +12,11 @@ import           Network.Syncthing.Types.Config      as Ty import           Network.Syncthing.Types.Connection  as Ty import           Network.Syncthing.Types.DeviceId    as Ty+import           Network.Syncthing.Types.DeviceInfo  as Ty import           Network.Syncthing.Types.DirTree     as Ty+import           Network.Syncthing.Types.DBFile      as Ty+import           Network.Syncthing.Types.FileInfo    as Ty+import           Network.Syncthing.Types.FolderInfo  as Ty import           Network.Syncthing.Types.Error       as Ty import           Network.Syncthing.Types.Ignore      as Ty import           Network.Syncthing.Types.Model       as Ty
Network/Syncthing/Internal/Utils.hs view
@@ -8,31 +8,25 @@     , fromUTC     ) where -import           Control.Applicative                     ((<$>))-import           Control.Arrow                           (second, (***))-import           Data.Maybe                              (fromMaybe)-import qualified Data.Text                               as T-import           Data.Time.Clock                         (UTCTime)-import           Data.Time.Format                        (formatTime, parseTime)-import           System.Locale                           (defaultTimeLocale)-import           Text.Regex.Posix                        ((=~))+import           Control.Applicative            ((<$>))+import           Data.Maybe                     (fromMaybe)+import qualified Data.Text                      as T+import           Data.Time.Clock                (UTCTime)+import           Data.Time.Format               (formatTime, parseTime)+import           System.Locale                  (defaultTimeLocale)+import           Text.Regex.Posix               ((=~))  import           Network.Syncthing.Types.Common   -- | Parse server string (SERVER:PORT) into an address type. parseAddr :: Server -> Addr-parseAddr s =-    if serverString =~ ("^[^:]+:[0-9]+$" :: String)-        then mapAddr . split (== ':') $ serverString-        else (T.pack serverString, Nothing)+parseAddr s = addr (serverString =~ serverPat :: [[String]])   where-    serverString = T.unpack s-    mapAddr :: (String, String) -> Addr-    mapAddr = T.pack *** (Just . read)--split :: (Char -> Bool) -> String -> (String, String)-split p = (second $ drop 1) . break p+    serverString     = T.unpack s+    serverPat        = "^\\[?([^]]+)\\]?:([0-9]+)$" :: String+    addr [[_, h, p]] = (T.pack h, Just $ read p)+    addr _           = (s, Nothing)  -- | Generate server string. encodeAddr :: Addr -> Server
Network/Syncthing/Post.hs view
@@ -16,19 +16,21 @@  module Network.Syncthing.Post     (-    -- * Request functions-      ping-    , bump-    , hint-    , sendConfig+    -- * System services+      config+    , discovery+    , ping     , sendError     , clearErrors-    , sendIgnores-    , scanFolder     , reset     , restart     , shutdown     , upgrade++    -- * Database Services+    , ignores+    , prio+    , scan     ) where  import           Control.Applicative                ((<$>))@@ -47,77 +49,75 @@  -- | Ping the Syncthing server. Returns the string \"pong\". ping :: MonadSync m => SyncM m Text-ping = getPing <$> ping'-  where-    ping' = query $ postRequest { path = "/rest/ping" }+ping = getPing <$> query postRequest { path = "/rest/system/ping" }  -- | Move the given file to the top of the download queue.-bump :: MonadSync m => FolderName -> Path -> SyncM m Need-bump folder filePath =-    query $ postRequest { path   = "/rest/bump"-                        , params = [ ("folder", folder) , ("file", filePath) ]-                        }+prio :: MonadSync m => FolderName -> Path -> SyncM m Need+prio folder filePath =+    query postRequest { path   = "/rest/db/prio"+                      , params = [ ("folder", folder) , ("file", filePath) ]+                      }  -- | Add an entry to the discovery cache.-hint:: MonadSync m => Device -> Server -> SyncM m ()-hint device server=-    send $ postRequest { path   = "/rest/discovery/hint"-                       , params = [("device", device), ("addr", server)]-                       }+discovery :: MonadSync m => Device -> Server -> SyncM m ()+discovery  device server =+    send postRequest { path   = "/rest/system/discovery"+                     , params = [("device", device), ("addr", server)]+                     }  -- | Update the server configuration. The configuration will be saved to -- disk and the configInSync flag set to false.  -- 'Network.Syncthing.Post.restart' Syncthing to activate.-sendConfig :: MonadSync m => Config -> SyncM m ()-sendConfig cfg = send $ postRequest { path   = "/rest/config"-                                    , method = post cfg-                                    }+config :: MonadSync m => Config -> SyncM m ()+config cfg = send postRequest { path   = "/rest/system/config"+                              , method = post cfg+                              }  -- | Register a new error message. sendError :: MonadSync m => Text -> SyncM m ()-sendError msg = send $ postRequest { path   = "/rest/error"-                                   , method = post msg-                                   }+sendError msg = send postRequest { path   = "/rest/system/error"+                                 , method = post msg+                                 }  -- | Remove all recent errors. clearErrors :: MonadSync m => SyncM m ()-clearErrors = send $ postRequest { path = "/rest/error/clear" }+clearErrors = send $ postRequest { path = "/rest/system/error/clear" }  -- | Update the ignores list and echo it back as response.-sendIgnores :: MonadSync m => FolderName -> [Text] -> SyncM m (Maybe [Text])-sendIgnores folder ignores =-    getIgnores <$> query postRequest { path   = "/rest/ignores"+ignores :: MonadSync m => FolderName -> [Text] -> SyncM m (Maybe [Text])+ignores folder ignoresList =+    getIgnores <$> query postRequest { path   = "/rest/db/ignores"                                      , method = post ignoresMap                                      , params = [("folder", folder)]                                      }   where     ignoresMap :: Map.Map Text [Text]-    ignoresMap = Map.singleton "ignore" ignores+    ignoresMap = Map.singleton "ignore" ignoresList  -- | Request rescan of a folder. Restrict the scan to a relative subpath -- within the folder by specifying the optional path parameter.-scanFolder:: MonadSync m => FolderName -> Maybe Path -> SyncM m ()-scanFolder folder subPath =-    send $ postRequest { path   = "/rest/scan"-                       , params = [("folder", folder)]-                                  ++ maybeToList (("sub",) <$> subPath)-                       }+scan:: MonadSync m => FolderName -> Maybe Path -> SyncM m ()+scan folder subPath =+    send postRequest { path   = "/rest/db/scan"+                     , params = [("folder", folder)]+                                ++ maybeToList (("sub",) <$> subPath)+                     }  -- | Restart Syncthing. restart :: MonadSync m => SyncM m SystemMsg-restart = query postRequest { path = "/rest/restart" }+restart = query postRequest { path = "/rest/system/restart" }  -- | Shutdown Syncthing. shutdown :: MonadSync m => SyncM m SystemMsg-shutdown = query postRequest { path = "/rest/shutdown" }+shutdown = query postRequest { path = "/rest/system/shutdown" }  -- | Reset Syncthing by renaming all folder directories to temporary, -- unique names, wiping all indexes and restarting. reset :: MonadSync m => SyncM m SystemMsg-reset = query postRequest { path = "/rest/reset" }+reset = query postRequest { path = "/rest/system/reset" }  -- | Perform an upgrade to the newest release and restart. Does nothing if -- there is no newer version. upgrade :: MonadSync m => SyncM m (Maybe SystemMsg)-upgrade = maybeSystemMsg $ postRequest { path = "/rest/upgrade" }+upgrade = maybeSystemMsg postRequest { path = "/rest/system/upgrade" } 
Network/Syncthing/Types.hs view
@@ -31,14 +31,19 @@     , VersioningConfig(..)     , GuiConfig(..)     , OptionsConfig(..)+    , Connections(..)     , Connection(..)+    , DeviceInfo(..)     , DirTree(..)     , Error(..)     , Ignore(..)     , Model(..)     , ModelState(..)     , Need(..)-    , Progress(..)+    , DBFile(..)+    , FileInfo(..)+    , FolderInfo(..)+    , LastFile(..)     , System(..)     , SystemMsg(..)     , Upgrade(..)
Network/Syncthing/Types/Config.hs view
@@ -39,20 +39,20 @@  instance FromJSON Config where     parseJSON (Object v) =-        Config <$> (v .: "Version")-               <*> (v .: "Folders")-               <*> (v .: "Devices")-               <*> (v .: "GUI")-               <*> (v .: "Options")+        Config <$> (v .: "version")+               <*> (v .: "folders")+               <*> (v .: "devices")+               <*> (v .: "gui")+               <*> (v .: "options")     parseJSON _          = mzero  instance ToJSON Config where     toJSON Config{..} =-        object [ "Version"  .= getConfigVersion-               , "Folders"  .= getFolderConfigs-               , "Devices"  .= getDeviceConfigs-               , "GUI"      .= getGuiConfig-               , "Options"  .= getOptionsConfig+        object [ "version"  .= getConfigVersion+               , "folders"  .= getFolderConfigs+               , "devices"  .= getDeviceConfigs+               , "gui"      .= getGuiConfig+               , "options"  .= getOptionsConfig                ]  @@ -87,41 +87,50 @@     , getReadOnly        :: Bool     , getRescanIntervalS :: Int     , getIgnorePerms     :: Bool+    , getAutoNormalize   :: Bool     , getVersioning      :: VersioningConfig     , getLenientMtimes   :: Bool     , getCopiers         :: Int     , getPullers         :: Int+    , getHashers         :: Int+    , getOrder           :: Text     , getFolderInvalid   :: Text     } deriving (Eq, Show)  instance FromJSON FolderConfig where     parseJSON (Object v) =-        FolderConfig <$> (v .: "ID")-                     <*> (v .: "Path")-                     <*> (map getFolderDevice <$> (v .: "Devices"))-                     <*> (v .: "ReadOnly")-                     <*> (v .: "RescanIntervalS")-                     <*> (v .: "IgnorePerms")-                     <*> (v .: "Versioning")-                     <*> (v .: "LenientMtimes")-                     <*> (v .: "Copiers")-                     <*> (v .: "Pullers")-                     <*> (v .: "Invalid")+        FolderConfig <$> (v .: "id")+                     <*> (v .: "path")+                     <*> (map getFolderDevice <$> (v .: "devices"))+                     <*> (v .: "readOnly")+                     <*> (v .: "rescanIntervalS")+                     <*> (v .: "ignorePerms")+                     <*> (v .: "autoNormalize")+                     <*> (v .: "versioning")+                     <*> (v .: "lenientMTimes")+                     <*> (v .: "copiers")+                     <*> (v .: "pullers")+                     <*> (v .: "hashers")+                     <*> (v .: "order")+                     <*> (v .: "invalid")     parseJSON _          = mzero  instance ToJSON FolderConfig where     toJSON FolderConfig{..} =-        object [ "ID"              .= getId-               , "Path"            .= getPath-               , "Devices"         .= map FolderDeviceConfig getFolderDevices-               , "ReadOnly"        .= getReadOnly-               , "RescanIntervalS" .= getRescanIntervalS-               , "IgnorePerms"     .= getIgnorePerms-               , "Versioning"      .= getVersioning-               , "LenientMtimes"   .= getLenientMtimes-               , "Copiers"         .= getCopiers-               , "Pullers"         .= getPullers-               , "Invalid"         .= getFolderInvalid+        object [ "id"              .= getId+               , "path"            .= getPath+               , "devices"         .= map FolderDeviceConfig getFolderDevices+               , "readOnly"        .= getReadOnly+               , "rescanIntervalS" .= getRescanIntervalS+               , "ignorePerms"     .= getIgnorePerms+               , "autoNormalize"   .= getAutoNormalize+               , "versioning"      .= getVersioning+               , "lenientMTimes"   .= getLenientMtimes+               , "copiers"         .= getCopiers+               , "pullers"         .= getPullers+               , "hashers"         .= getHashers+               , "order"           .= getOrder+               , "invalid"         .= getFolderInvalid                ]  @@ -137,14 +146,14 @@  instance FromJSON VersioningConfig where     parseJSON (Object v) =-        VersioningConfig <$> (v .: "Type")-                         <*> (v .: "Params")+        VersioningConfig <$> (v .: "type")+                         <*> (v .: "params")     parseJSON _          = mzero  instance ToJSON VersioningConfig where     toJSON VersioningConfig{..} =-        object [ "Type"   .= getType-               , "Params" .= getParams+        object [ "type"   .= getType+               , "params" .= getParams                ]  @@ -157,29 +166,29 @@       getDevice      :: Device     , getDeviceName  :: Text     , getAddresses   :: [AddressType]-    , getCompression :: Bool+    , getCompression :: Text     , getCertName    :: Text     , getIntroducer  :: Bool     } deriving (Eq, Show)  instance FromJSON DeviceConfig where     parseJSON (Object v) =-        DeviceConfig <$> (v .: "DeviceID")-                     <*> (v .: "Name")-                     <*> (map decodeAddressType <$> (v .: "Addresses"))-                     <*> (v .: "Compression")-                     <*> (v .: "CertName")-                     <*> (v .: "Introducer")+        DeviceConfig <$> (v .: "deviceID")+                     <*> (v .: "name")+                     <*> (map decodeAddressType <$> (v .: "addresses"))+                     <*> (v .: "compression")+                     <*> (v .: "certName")+                     <*> (v .: "introducer")     parseJSON _          = mzero  instance ToJSON DeviceConfig where     toJSON DeviceConfig{..} =-        object [ "DeviceID"     .= getDevice-               , "Name"         .= getDeviceName-               , "Addresses"    .= map encodeAddressType getAddresses-               , "Compression"  .= getCompression-               , "CertName"     .= getCertName-               , "Introducer"   .= getIntroducer+        object [ "deviceID"     .= getDevice+               , "name"         .= getDeviceName+               , "addresses"    .= map encodeAddressType getAddresses+               , "compression"  .= getCompression+               , "certName"     .= getCertName+               , "introducer"   .= getIntroducer                ]  @@ -192,12 +201,12 @@     } deriving (Eq, Show)  instance FromJSON FolderDeviceConfig where-    parseJSON (Object v) = FolderDeviceConfig <$> (v .: "DeviceID")+    parseJSON (Object v) = FolderDeviceConfig <$> (v .: "deviceID")     parseJSON _          = mzero  instance ToJSON FolderDeviceConfig where     toJSON (FolderDeviceConfig device) =-        object [ "DeviceID" .= device ]+        object [ "deviceID" .= device ]   -------------------------------------------------------------------------------@@ -216,22 +225,22 @@  instance FromJSON GuiConfig where     parseJSON (Object v) =-        GuiConfig <$> (v .: "Enabled")-                  <*> (decodeApiKey <$> (v .: "APIKey"))-                  <*> (parseAddr <$> (v .: "Address"))-                  <*> (v .: "User")-                  <*> (v .: "Password")-                  <*> (v .: "UseTLS")+        GuiConfig <$> (v .: "enabled")+                  <*> (decodeApiKey <$> (v .: "apiKey"))+                  <*> (parseAddr <$> (v .: "address"))+                  <*> (v .: "user")+                  <*> (v .: "password")+                  <*> (v .: "useTLS")     parseJSON _          = mzero  instance ToJSON GuiConfig where     toJSON GuiConfig{..} =-        object [ "Enabled"  .= getEnabled-               , "APIKey"   .= encodeApiKey getApiKey-               , "Address"  .= encodeAddr getGuiAddress-               , "User"     .= getUser-               , "Password" .= getPassword-               , "UseTLS"   .= getUseTLS+        object [ "enabled"  .= getEnabled+               , "apiKey"   .= encodeApiKey getApiKey+               , "address"  .= encodeAddr getGuiAddress+               , "user"     .= getUser+               , "password" .= getPassword+               , "useTLS"   .= getUseTLS                ]  decodeApiKey :: Text -> Maybe Text@@ -248,75 +257,81 @@ -- | Various config settings. data OptionsConfig = OptionsConfig {       getListenAddress           :: [Addr]-    , getGlobalAnnServers        :: [Text]-    , getGlobalAnnEnabled        :: Bool-    , getLocalAnnEnabled         :: Bool-    , getLocalAnnPort            :: Int-    , getLocalAnnMCAddr          :: Text+    , getGlobalAnnounceServers   :: [Text]+    , getGlobalAnnounceEnabled   :: Bool+    , getLocalAnnounceEnabled    :: Bool+    , getLocalAnnouncePort       :: Int+    , getLocalAnnounceMCAddr     :: Text     , getMaxSendKbps             :: Int     , getMaxRecvKbps             :: Int-    , getReconnectIntervalS      :: Int+    , getReconnectionIntervalS   :: Int     , getStartBrowser            :: Bool-    , getUPnPEnabled             :: Bool-    , getUPnPLease               :: Int-    , getUPnPRenewal             :: Int-    , getURAccepted              :: Int-    , getURUniqueID              :: Text+    , getUpnpEnabled             :: Bool+    , getUpnpLeaseMinutes        :: Int+    , getUpnpRenewalMinutes      :: Int+    , getUpnpTimeoutSeconds      :: Int+    , getUrAccepted              :: Int+    , getUrUniqueID              :: Text     , getRestartOnWakeup         :: Bool     , getAutoUpgradeIntervalH    :: Int     , getKeepTemporariesH        :: Int     , getCacheIgnoredFiles       :: Bool     , getProgressUpdateIntervalS :: Int     , getSymlinksEnabled         :: Bool+    , getLimitBandwidthInLan     :: Bool } deriving (Eq, Show)  instance FromJSON OptionsConfig where     parseJSON (Object v) =-        OptionsConfig <$> (map parseAddr <$> (v .: "ListenAddress"))-                      <*> (v .: "GlobalAnnServers")-                      <*> (v .: "GlobalAnnEnabled")-                      <*> (v .: "LocalAnnEnabled")-                      <*> (v .: "LocalAnnPort")-                      <*> (v .: "LocalAnnMCAddr")-                      <*> (v .: "MaxSendKbps")-                      <*> (v .: "MaxRecvKbps")-                      <*> (v .: "ReconnectIntervalS")-                      <*> (v .: "StartBrowser")-                      <*> (v .: "UPnPEnabled")-                      <*> (v .: "UPnPLease")-                      <*> (v .: "UPnPRenewal")-                      <*> (v .: "URAccepted")-                      <*> (v .: "URUniqueID")-                      <*> (v .: "RestartOnWakeup")-                      <*> (v .: "AutoUpgradeIntervalH")-                      <*> (v .: "KeepTemporariesH")-                      <*> (v .: "CacheIgnoredFiles")-                      <*> (v .: "ProgressUpdateIntervalS")-                      <*> (v .: "SymlinksEnabled")+        OptionsConfig <$> (map parseAddr <$> (v .: "listenAddress"))+                      <*> (v .: "globalAnnounceServers")+                      <*> (v .: "globalAnnounceEnabled")+                      <*> (v .: "localAnnounceEnabled")+                      <*> (v .: "localAnnouncePort")+                      <*> (v .: "localAnnounceMCAddr")+                      <*> (v .: "maxSendKbps")+                      <*> (v .: "maxRecvKbps")+                      <*> (v .: "reconnectionIntervalS")+                      <*> (v .: "startBrowser")+                      <*> (v .: "upnpEnabled")+                      <*> (v .: "upnpLeaseMinutes")+                      <*> (v .: "upnpRenewalMinutes")+                      <*> (v .: "upnpTimeoutSeconds")+                      <*> (v .: "urAccepted")+                      <*> (v .: "urUniqueId")+                      <*> (v .: "restartOnWakeup")+                      <*> (v .: "autoUpgradeIntervalH")+                      <*> (v .: "keepTemporariesH")+                      <*> (v .: "cacheIgnoredFiles")+                      <*> (v .: "progressUpdateIntervalS")+                      <*> (v .: "symlinksEnabled")+                      <*> (v .: "limitBandwidthInLan")     parseJSON _          = mzero  instance ToJSON OptionsConfig where     toJSON OptionsConfig{..} =-        object [ "ListenAddress"           .= map encodeAddr getListenAddress-               , "GlobalAnnServers"        .= getGlobalAnnServers-               , "GlobalAnnEnabled"        .= getGlobalAnnEnabled-               , "LocalAnnEnabled"         .= getLocalAnnEnabled-               , "LocalAnnPort"            .= getLocalAnnPort-               , "LocalAnnMCAddr"          .= getLocalAnnMCAddr-               , "MaxSendKbps"             .= getMaxSendKbps-               , "MaxRecvKbps"             .= getMaxRecvKbps-               , "ReconnectIntervalS"      .= getReconnectIntervalS-               , "StartBrowser"            .= getStartBrowser-               , "UPnPEnabled"             .= getUPnPEnabled-               , "UPnPLease"               .= getUPnPLease-               , "UPnPRenewal"             .= getUPnPRenewal-               , "URAccepted"              .= getURAccepted-               , "URUniqueID"              .= getURUniqueID-               , "RestartOnWakeup"         .= getRestartOnWakeup-               , "AutoUpgradeIntervalH"    .= getAutoUpgradeIntervalH-               , "KeepTemporariesH"        .= getKeepTemporariesH-               , "CacheIgnoredFiles"       .= getCacheIgnoredFiles-               , "ProgressUpdateIntervalS" .= getProgressUpdateIntervalS-               , "SymlinksEnabled"         .= getSymlinksEnabled+        object [ "listenAddress"           .= map encodeAddr getListenAddress+               , "globalAnnounceServers"   .= getGlobalAnnounceServers+               , "globalAnnounceEnabled"   .= getGlobalAnnounceEnabled+               , "localAnnounceEnabled"    .= getLocalAnnounceEnabled+               , "localAnnouncePort"       .= getLocalAnnouncePort+               , "localAnnounceMCAddr"     .= getLocalAnnounceMCAddr+               , "maxSendKbps"             .= getMaxSendKbps+               , "maxRecvKbps"             .= getMaxRecvKbps+               , "reconnectionIntervalS"   .= getReconnectionIntervalS+               , "startBrowser"            .= getStartBrowser+               , "upnpEnabled"             .= getUpnpEnabled+               , "upnpLeaseMinutes"        .= getUpnpLeaseMinutes+               , "upnpRenewalMinutes"      .= getUpnpRenewalMinutes+               , "upnpTimeoutSeconds"      .= getUpnpTimeoutSeconds+               , "urAccepted"              .= getUrAccepted+               , "urUniqueId"              .= getUrUniqueID+               , "restartOnWakeup"         .= getRestartOnWakeup+               , "autoUpgradeIntervalH"    .= getAutoUpgradeIntervalH+               , "keepTemporariesH"        .= getKeepTemporariesH+               , "cacheIgnoredFiles"       .= getCacheIgnoredFiles+               , "progressUpdateIntervalS" .= getProgressUpdateIntervalS+               , "symlinksEnabled"         .= getSymlinksEnabled+               , "limitBandwidthInLan"     .= getLimitBandwidthInLan                ] 
Network/Syncthing/Types/Connection.hs view
@@ -3,15 +3,17 @@  module Network.Syncthing.Types.Connection     ( Connection(..)+    , Connections(..)     ) where  import           Control.Applicative              ((<$>), (<*>)) import           Control.Monad                    (MonadPlus (mzero)) import           Data.Aeson                       (FromJSON, Value (..), parseJSON, (.:))+import qualified Data.Map                         as M import           Data.Text                        (Text) import           Data.Time.Clock                  (UTCTime) -import           Network.Syncthing.Types.Common   (Addr)+import           Network.Syncthing.Types.Common   (Addr, Device) import           Network.Syncthing.Internal.Utils (parseAddr, toUTC)  @@ -26,10 +28,22 @@  instance FromJSON Connection where     parseJSON (Object v) =-        Connection <$> (toUTC <$> (v .:  "At"))-                   <*> (v .:  "InBytesTotal")-                   <*> (v .:  "OutBytesTotal")-                   <*> (parseAddr <$> (v .:  "Address"))-                   <*> (v .:  "ClientVersion")+        Connection <$> (toUTC <$> (v .:  "at"))+                   <*> (v .:  "inBytesTotal")+                   <*> (v .:  "outBytesTotal")+                   <*> (parseAddr <$> (v .:  "address"))+                   <*> (v .:  "clientVersion")+    parseJSON _          = mzero++-- | Contains the list of current connections.+data Connections = Connections {+      getConnections    :: M.Map Device Connection+    , getTotal          :: Connection+    } deriving (Eq, Show)++instance FromJSON Connections where+    parseJSON (Object v) =+        Connections <$> (v .:  "connections")+                    <*> (v .:  "total")     parseJSON _          = mzero 
+ Network/Syncthing/Types/DBFile.hs view
@@ -0,0 +1,29 @@++{-# LANGUAGE OverloadedStrings #-}++module Network.Syncthing.Types.DBFile+    ( DBFile(..)+    ) where++import           Control.Applicative              ((<$>), (<*>))+import           Control.Monad                    (MonadPlus (mzero))+import           Data.Aeson                       (FromJSON, Value (..), parseJSON, (.:))++import           Network.Syncthing.Types.Common   (Device)+import           Network.Syncthing.Types.FileInfo (FileInfo)+++-- | Contains data available about a given file.+data DBFile = DBFile {+      getAvailability   :: [Device]+    , getGlobal         :: FileInfo+    , getLocal          :: FileInfo+    } deriving (Eq, Show)++instance FromJSON DBFile where+    parseJSON (Object v) =+        DBFile <$> (v .: "availability")+               <*> (v .: "global")+               <*> (v .: "local")+    parseJSON _          = mzero+
+ Network/Syncthing/Types/DeviceInfo.hs view
@@ -0,0 +1,24 @@++{-# LANGUAGE OverloadedStrings #-}++module Network.Syncthing.Types.DeviceInfo+    ( DeviceInfo(..)+    ) where++import           Control.Applicative              ((<$>))+import           Control.Monad                    (MonadPlus (mzero))+import           Data.Aeson                       (FromJSON, Value (..), parseJSON, (.:))+import           Data.Time.Clock                  (UTCTime)++import           Network.Syncthing.Internal.Utils (toUTC)+++-- | Contains information about a device.+data DeviceInfo = DeviceInfo    {+      getLastSeen   :: Maybe UTCTime+    } deriving (Eq, Show)++instance FromJSON DeviceInfo where+    parseJSON (Object v) = DeviceInfo <$> (toUTC <$> v .: "lastSeen")+    parseJSON _          = mzero+
Network/Syncthing/Types/DirTree.hs view
@@ -5,31 +5,32 @@     ( DirTree(..)     ) where -import           Control.Applicative ((<$>))-import           Control.Monad       (MonadPlus (mzero))-import           Data.Aeson          (FromJSON, Value (..), parseJSON)-import qualified Data.Map            as M-import           Data.Text           (Text)+import           Control.Applicative              ((<$>), (<*>))+import           Control.Monad                    (MonadPlus (mzero))+import           Data.Aeson                       (FromJSON, Value (..),+                                                   parseJSON)+import qualified Data.Map                         as M+import           Data.Text                        (Text)+import           Data.Time.Clock                  (UTCTime)+import           Data.Vector                      ((!)) +import           Network.Syncthing.Internal.Utils (toUTC) + -- | A directory tree contains files or subdirectories.-data DirTree +data DirTree     = Dir {         getDirContents :: M.Map Text DirTree       }     | File {-        getModTime  :: Integer    -- ^ file modification time-      , getFileSize :: Integer    -- ^ file size-      } +        getModTime  :: Maybe UTCTime    -- ^ file modification time+      , getFileSize :: Integer          -- ^ file size+      }     deriving (Eq, Show)  instance FromJSON DirTree where-    parseJSON obj@(Object _) = Dir <$> parseJSON obj-    parseJSON arr@(Array _)  = decodeFile <$> parseJSON arr+    parseJSON obj@(Object _) = Dir  <$> parseJSON obj+    parseJSON (Array v)      = File <$> (toUTC <$> parseJSON (v ! 0))+                                    <*> parseJSON (v ! 1)     parseJSON _              = mzero---decodeFile :: [Integer] -> DirTree-decodeFile [modTime, fileSize] = File modTime fileSize-decodeFile _                   = File 0 0 
Network/Syncthing/Types/Error.hs view
@@ -26,8 +26,8 @@  instance FromJSON Error where     parseJSON (Object v) =-        Error <$> (toUTC <$> (v .:  "Time"))-              <*> (v .:  "Error")+        Error <$> (toUTC <$> (v .:  "time"))+              <*> (v .:  "error")     parseJSON _          = mzero  instance FromJSON Errors where
+ Network/Syncthing/Types/FileInfo.hs view
@@ -0,0 +1,39 @@++{-# LANGUAGE OverloadedStrings #-}++module Network.Syncthing.Types.FileInfo+    ( FileInfo(..)+    ) where++import           Control.Applicative              ((<$>), (<*>))+import           Control.Monad                    (MonadPlus (mzero))+import           Data.Aeson                       (FromJSON, Value (..),+                                                   parseJSON, (.:), (.:?))+import           Data.Text                        (Text)+import           Data.Time.Clock                  (UTCTime)++import           Network.Syncthing.Internal.Utils (toUTC)+++-- | All available information about a file.+data FileInfo = FileInfo {+      getName         :: Text+    , getFlags        :: Text+    , getModified     :: Maybe UTCTime+    , getFileVersion  :: [Text]+    , getLocalVersion :: Int+    , getSize         :: Integer+    , getNumBlocks    :: Maybe Int+    } deriving (Eq, Show)++instance FromJSON FileInfo where+    parseJSON (Object v) =+        FileInfo <$> (v .: "name")+                 <*> (v .: "flags")+                 <*> (toUTC <$> (v .: "modified"))+                 <*> (v .: "version")+                 <*> (v .: "localVersion")+                 <*> (v .: "size")+                 <*> (v .:? "numBlocks")+    parseJSON _          = mzero+
+ Network/Syncthing/Types/FolderInfo.hs view
@@ -0,0 +1,38 @@++{-# LANGUAGE OverloadedStrings #-}++module Network.Syncthing.Types.FolderInfo+    ( FolderInfo(..)+    , LastFile(..)+    ) where++import           Control.Applicative              ((<$>), (<*>))+import           Control.Monad                    (MonadPlus (mzero))+import           Data.Aeson                       (FromJSON, Value (..), parseJSON, (.:))+import           Data.Text                        (Text)+import           Data.Time.Clock                  (UTCTime)++import           Network.Syncthing.Internal.Utils (toUTC)+++-- | Contains general statistics about folders.+data FolderInfo = FolderInfo {+      getLastFile :: LastFile+    } deriving (Eq, Show)++-- | Information about the last synced file.+data LastFile = LastFile {+      getFileName  :: Text+    , getSyncedAt  :: Maybe UTCTime+    } deriving (Eq, Show)++instance FromJSON FolderInfo where+    parseJSON (Object v) = FolderInfo <$> (v .:  "lastFile")+    parseJSON _          = mzero++instance FromJSON LastFile where+    parseJSON (Object v) =+        LastFile <$> (v .: "filename")+                 <*> (toUTC <$> (v .: "at"))+    parseJSON _          = mzero+
Network/Syncthing/Types/Need.hs view
@@ -3,44 +3,22 @@  module Network.Syncthing.Types.Need     ( Need(..)-    , Progress(..)     ) where -import           Control.Applicative ((<$>), (<*>))-import           Control.Monad       (MonadPlus (mzero))-import           Data.Aeson          (FromJSON, Value (..), parseJSON, (.:))-import           Data.Text           (Text)+import           Control.Applicative              ((<$>), (<*>))+import           Control.Monad                    (MonadPlus (mzero))+import           Data.Aeson                       (FromJSON, Value (..), parseJSON, (.:)) +import           Network.Syncthing.Types.FileInfo (FileInfo) + -- | Contains lists of files which are needed by a device for becoming in -- sync. data Need = Need {-      getProgress :: [Progress]-    , getQueued   :: [Text]-    , getRest     :: [Text]-    } deriving (Eq, Show)---- | A file that is currently downloading. -data Progress = Progress { -      getName            :: Text-    , getFlags           :: Int-    , getModified        :: Integer-    , getProgressVersion :: Int-    , getLocalVersion    :: Int-    , getNumBlocks       :: Int-    , getSize            :: Integer+      getProgress :: [FileInfo]+    , getQueued   :: [FileInfo]+    , getRest     :: [FileInfo]     } deriving (Eq, Show)--instance FromJSON Progress where-    parseJSON (Object v) =-        Progress <$> (v .: "Name")-                 <*> (v .: "Flags")-                 <*> (v .: "Modified")-                 <*> (v .: "Version")-                 <*> (v .: "LocalVersion")-                 <*> (v .: "NumBlocks")-                 <*> (v .: "Size")-    parseJSON _          = mzero  instance FromJSON Need where     parseJSON (Object v) =
Network/Syncthing/Types/System.hs view
@@ -22,6 +22,9 @@     , getGoRoutines    :: Int     , getMyId          :: Text     , getSys           :: Integer+    , getPathSeparator :: Text+    , getTilde         :: Text+    , getUptime        :: Integer     } deriving (Eq, Show)  instance FromJSON System where@@ -32,5 +35,8 @@                <*> (v .:  "goroutines")                <*> (v .:  "myID")                <*> (v .:  "sys")+               <*> (v .:  "pathSeparator")+               <*> (v .:  "tilde")+               <*> (v .:  "uptime")     parseJSON _          = mzero 
README.md view
@@ -26,9 +26,9 @@ ``` haskell {-# LANGUAGE OverloadedStrings #-} -import qualified Network.Wreq as Wreq-import Control.Monad (liftM2) import Control.Lens ((&), (.~), (?~))+import Control.Monad (liftM2)+import qualified Network.Wreq as Wreq import Network.Syncthing import qualified Network.Syncthing.Get as Get 
changelog.md view
@@ -1,3 +1,8 @@+0.2.0.0+-------+* Update library for Syncthing v0.11+* Refactor package internals+ 0.1.2.0 ------- * Update lower bounds of package dependencies 
syncthing-hs.cabal view
@@ -1,5 +1,5 @@ name:                syncthing-hs-version:             0.1.2.0+version:             0.2.0.0 synopsis:            Haskell bindings for the Syncthing REST API description:              .@@ -40,7 +40,11 @@                      Network.Syncthing.Types.Config                      Network.Syncthing.Types.Connection                      Network.Syncthing.Types.DeviceId+                     Network.Syncthing.Types.DeviceInfo                      Network.Syncthing.Types.DirTree+                     Network.Syncthing.Types.DBFile+                     Network.Syncthing.Types.FileInfo+                     Network.Syncthing.Types.FolderInfo                      Network.Syncthing.Types.Error                      Network.Syncthing.Types.Ignore                      Network.Syncthing.Types.Model@@ -69,6 +73,7 @@                      , time >=1.4.2                      , transformers >=0.2.2.1                      , unordered-containers >=0.2.3.3+                     , vector >= 0.10.12.3                      , wreq >=0.3.0.0   -- hs-source-dirs:       default-language:  Haskell2010@@ -80,16 +85,22 @@                      , base >=4.5 && <5                      , bytestring >=0.9                      , containers >= 0.5.5.1+                     , data-default                      , derive                        , either >=4.0+                     , exceptions >= 0.5                      , lens >=4.5 +                     , http-client >=0.4.6+                     , http-types >= 0.8                      , quickcheck-instances+                     , scientific >= 0.3.3.8                      , syncthing-hs                      , tasty                      , tasty-hunit                      , tasty-quickcheck                      , text >=1.1.1.0                      , transformers >=0.2.2.1+                     , vector >= 0.10.12.3                      , wreq >=0.3.0.0   other-modules:     Properties.ErrorProperties                      Properties.JsonArbitrary
tests/Properties/JsonArbitrary.hs view
@@ -111,6 +111,8 @@                       <*> arbitrary                       <*> arbitrary                       <*> arbitrary+                      <*> arbitrary+                      <*> arbitrary  instance Arbitrary DirTree where     arbitrary = choose (0, 5) >>= dirTree@@ -138,7 +140,8 @@                 , ''ModelState                 , ''Upgrade                 , ''Ignore-                , ''Progress+                , ''DBFile+                , ''FileInfo                 , ''Need                 , ''Sync                 , ''DeviceError@@ -146,5 +149,9 @@                 , ''Errors                 , ''System                 , ''UsageReport+                , ''Connections+                , ''DeviceInfo+                , ''FolderInfo+                , ''LastFile                 ] 
tests/Properties/JsonInstances.hs view
@@ -8,8 +8,10 @@  import           Control.Applicative              ((<$>), pure) import           Data.Aeson                       hiding (Error)-import           Data.Maybe                       (fromMaybe)+import           Data.Maybe                       (fromMaybe, maybeToList)+import           Data.Scientific                  (scientific) import qualified Data.Text                        as T+import qualified Data.Vector                      as V  import           Network.Syncthing.Internal @@ -56,13 +58,19 @@  instance ToJSON Connection where     toJSON Connection{..} =-        object [ "At"            .= encodeUTC getAt-               , "InBytesTotal"  .= getInBytesTotal-               , "OutBytesTotal" .= getOutBytesTotal-               , "Address"       .= encodeAddr getAddress-               , "ClientVersion" .= getClientVersion+        object [ "at"            .= encodeUTC getAt+               , "inBytesTotal"  .= getInBytesTotal+               , "outBytesTotal" .= getOutBytesTotal+               , "address"       .= encodeAddr getAddress+               , "clientVersion" .= getClientVersion                ] +instance ToJSON Connections where+    toJSON Connections{..} =+        object [ "connections"   .= getConnections+               , "total"         .= getTotal+               ]+ instance ToJSON Model where     toJSON Model{..} =         object [ "globalBytes"   .= getGlobalBytes   @@ -101,15 +109,22 @@                , "rest"     .= getRest                ] -instance ToJSON Progress where-    toJSON Progress{..} =-        object [ "Name"         .= getName            -               , "Flags"        .= getFlags           -               , "Modified"     .= getModified        -               , "Version"      .= getProgressVersion -               , "LocalVersion" .= getLocalVersion    -               , "NumBlocks"    .= getNumBlocks       -               , "Size"         .= getSize            +instance ToJSON FileInfo where+    toJSON FileInfo{..} =+        object $ [ "name"         .= getName            +                 , "flags"        .= getFlags           +                 , "modified"     .= encodeUTC getModified        +                 , "version"      .= getFileVersion +                 , "localVersion" .= getLocalVersion    +                 , "size"         .= getSize            +                 ]+                 ++ maybeToList (("numBlocks" .=) <$> getNumBlocks)++instance ToJSON DBFile where+    toJSON DBFile{..} =+        object [ "availability" .= getAvailability+               , "global"       .= getGlobal+               , "local"        .= getLocal                ]  instance ToJSON System where@@ -120,6 +135,9 @@                , "goroutines"       .= getGoRoutines                 , "myID"             .= getMyId                    , "sys"              .= getSys       +               , "pathSeparator"    .= getPathSeparator+               , "tilde"            .= getTilde       +               , "uptime"           .= getUptime                ]  instance ToJSON (Either DeviceError Device) where@@ -145,16 +163,31 @@  instance ToJSON Error where     toJSON Error{..} =-        object [ "Time"  .= encodeUTC getTime-               , "Error" .= getMsg+        object [ "time"  .= encodeUTC getTime+               , "error" .= getMsg                ]  instance ToJSON Errors where     toJSON = singleField "errors" . getErrors  +instance ToJSON DeviceInfo where+    toJSON DeviceInfo{..} = object [ "lastSeen" .= encodeUTC getLastSeen ]+    +instance ToJSON FolderInfo where+    toJSON FolderInfo{..} = object [ "lastFile" .= getLastFile ]++instance ToJSON LastFile where+    toJSON LastFile{..} = +        object [ "filename" .= getFileName+               , "at"       .= encodeUTC getSyncedAt +               ]+ instance ToJSON DirTree where     toJSON Dir{..}  = toJSON getDirContents-    toJSON File{..} = toJSON [getModTime, getFileSize] +    toJSON File{..} = Array $ V.fromList [modTime, fileSize]+      where+        modTime  = String . T.pack . encodeUTC $ getModTime+        fileSize = Number $ scientific getFileSize 0  instance ToJSON UsageReport where     toJSON UsageReport{..} =
tests/Properties/JsonProperties.hs view
@@ -30,10 +30,12 @@     , genProp "Completion"       (prop_json :: Completion -> Bool)     , genProp "CacheEntry"       (prop_json :: CacheEntry -> Bool)     , genProp "Connection"       (prop_json :: Connection -> Bool)+    , genProp "Connections"      (prop_json :: Connections -> Bool)     , genProp "Model"            (prop_json :: Model -> Bool)     , genProp "Upgrade"          (prop_json :: Upgrade -> Bool)     , genProp "Ignore"           (prop_json :: Ignore -> Bool)-    , genProp "Progress"         (prop_json :: Progress -> Bool)+    , genProp "FileInfo"         (prop_json :: FileInfo -> Bool)+    , genProp "DBFile"           (prop_json :: DBFile -> Bool)     , genProp "Need"             (prop_json :: Need -> Bool)     , genProp "Sync"             (prop_json :: Sync -> Bool)     , genProp "DeviceId"         (prop_json :: EitherDeviceErrorId -> Bool)@@ -49,5 +51,8 @@     , genProp "System"           (prop_json :: System -> Bool)     , genProp "DirTree"          (prop_json :: DirTree -> Bool)     , genProp "UsageReport"      (prop_json :: UsageReport -> Bool)+    , genProp "DeviceInfo"       (prop_json :: DeviceInfo -> Bool)+    , genProp "FolderInfo"       (prop_json :: FolderInfo -> Bool)+    , genProp "LastFile"         (prop_json :: LastFile -> Bool)     ] 
tests/UnitTests/Errors.hs view
@@ -1,53 +1,116 @@ +{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-}  module UnitTests.Errors     ( errorUnits     ) where -import qualified Data.ByteString.Lazy.Char8 as BS+import           Control.Monad.Catch.Pure+import           Control.Monad.Trans.Class  (lift)+import qualified Data.ByteString            as BS+import           Data.Default               (def) import qualified Data.Text                  as T+import           Network.HTTP.Client+import           Network.HTTP.Types import           Test.Tasty import           Test.Tasty.HUnit +import           Network.Syncthing+import qualified Network.Syncthing.Get      as Get import           Network.Syncthing.Internal  -createTestName :: Show err => err -> String-createTestName = ("Decode " ++) . show -createDescription :: Show err => err -> String -> String-createDescription errType errMsg =-    concat [show errMsg, " decodes to ", show errType]+-------------- Test Infrastructure -------------- -testDecodeError :: SyncError -> BS.ByteString -> TestTree-testDecodeError errType errMsg =-    testCase (createTestName errType) $-        assertEqual (createDescription errType $ BS.unpack errMsg)-                    (Just errType)-                    (decodeError errMsg)+type CatchM        = SyncM Catch+type CatchResult a = Either SomeException (SyncResult a) -testDecodeDeviceError :: DeviceError -> T.Text -> TestTree-testDecodeDeviceError errType errMsg =-    testCase (createTestName errType) $-        assertEqual (createDescription errType $ T.unpack errMsg)-                    errType-                    (decodeDeviceError errMsg) +instance MonadSync Catch where+    getMethod  o s   = return "{\"invalidKey\":\"pong\"}"+    postMethod o s p = return "{\"invalidKey\":\"pong\"}" +instance MonadThrow CatchM where+    throwM = SyncM . throwM ++requestWithException :: Exception ex => ex -> CatchM a -> SyncM Catch a+requestWithException ex action = throwM ex >> action++runRequest :: SyncConfig -> CatchM a -> CatchResult a+runRequest cfg action = runCatch $ syncthingM cfg action++++-------------- Helpers --------------++pingWithEx :: Exception ex => ex -> CatchResult T.Text+pingWithEx ex = runRequest defaultConfig $ requestWithException ex Get.ping++statusCodeEx :: Status -> BS.ByteString -> HttpException+statusCodeEx status msg =+    StatusCodeException status [("X-Response-Body-Start", msg)] def++testParseEx =+    testCase "Handle ParseError" $+        assertBool "JSON parse errors are handled correctly" $+            isHandled $ runRequest defaultConfig Get.ping+  where+    isHandled (Right (Left (ParseError _))) = True+    isHandled _                             = False++testHandledEx :: Exception ex => SyncError -> ex -> TestTree+testHandledEx errType ex =+    testCase ("Handle " ++ show errType) $+        assertBool "Exception is handled correctly" $+            isHandled (pingWithEx ex)+  where+    isHandled (Right (Left err)) = errType == err+    isHandled _                  = False++testUnhandledEx :: Exception ex => ex -> TestTree+testUnhandledEx ex =+    testCase ("Unhandled Exception: " ++ show ex) $+        assertBool "Exception is not handled" $+            isNotHandled (pingWithEx ex)+  where+    isNotHandled (Left _) = True+    isNotHandled _        = False++testDeviceError :: DeviceError -> T.Text -> TestTree+testDeviceError errType errMsg =+    testCase ("Decoding " ++ show errType) $+        assertEqual description errType (decodeDeviceError errMsg)+  where+    description = concat [show (T.unpack errMsg), " decodes to ", show errType]++++-------------- Test Suite --------------+ errorUnits :: TestTree-errorUnits = testGroup "Unit Tests for error messages" $-    map (uncurry testDecodeError)-    [ (CSRFError, "CSRF Error")-    , (NotAuthorized, "Not Authorized")-    , (NotFound, "404 page not found")-    , (NoSuchFolder, "no such folder")-    , ((InvalidDeviceId IncorrectLength), "device ID invalid: incorrect length")-    , ((InvalidDeviceId IncorrectCheckDigit), "check digit incorrect")-    ]-    ++-    map (uncurry testDecodeDeviceError)-    [ (IncorrectLength, "device ID invalid: incorrect length")-    , (IncorrectCheckDigit, "check digit incorrect")+errorUnits = testGroup "Error Handling Unit Tests"+    [ testGroup "Handled Exceptions"+        [ testParseEx+        , testHandledEx CSRFError $ statusCodeEx status403 "CSRF Error"+        , testHandledEx NotAuthorized $ statusCodeEx status401 "Not Authorized"+        , testHandledEx NotFound $ statusCodeEx status404 "404 page not found"+        , testHandledEx NoSuchFolder $ statusCodeEx status500 "no such folder"+        , testHandledEx (InvalidDeviceId IncorrectLength)+            (statusCodeEx status500 "device ID invalid: incorrect length")+        , testHandledEx (InvalidDeviceId IncorrectCheckDigit)+            (statusCodeEx status500 "check digit incorrect")+        ]+    , testGroup "Unhandled Exceptions"+        [ testUnhandledEx $ statusCodeEx status500 "unknown Error"+        , testUnhandledEx TooManyRetries+        , testUnhandledEx ResponseTimeout+        , testUnhandledEx $ FailedConnectionException "127.0.0.1" 8080+        ]+    , testGroup "Decoding Device Errors"+        [ testDeviceError IncorrectLength "device ID invalid: incorrect length"+        , testDeviceError IncorrectCheckDigit "check digit incorrect"+        ]     ] 
tests/UnitTests/Requests.hs view
@@ -9,8 +9,6 @@  import           Control.Applicative        ((<$>)) import           Control.Lens               ((&), (.~), (?~), (^.))-import           Control.Monad.Trans.Either (runEitherT)-import           Control.Monad.Trans.Reader (runReaderT) import           Control.Monad.Trans.Writer (Writer, execWriter, tell) import           Data.Aeson                 (ToJSON, Value, toJSON) import           Data.List                  (isPrefixOf, sort)@@ -57,8 +55,7 @@     postMethod o s p = tell [ LoggedRequest POST s o (Just p) ] >> return ""  mockedSyncthing :: SyncConfig -> LogAction a -> LogResult a-mockedSyncthing config action =-    flip runReaderT config $ runEitherT $ runSyncthing action+mockedSyncthing = runSyncM  extractRequest :: LogResult a -> LoggedRequest extractRequest = head . execWriter@@ -124,7 +121,7 @@  -------------- Basic Tests -------------- -basicRequest rType endpoint action  = withRequest action $+basicReq rType endpoint action  = withRequest action $     \LoggedRequest{..} -> do         assertReqType rType reqType          assertUrl defaultConfig endpoint reqUrl@@ -136,7 +133,7 @@         cfg    = defaultConfig & pServer .~ server     in     withConfigRequest cfg Get.ping $-        \LoggedRequest{..} -> assertUrl cfg "/rest/ping" reqUrl+        \LoggedRequest{..} -> assertUrl cfg "/rest/system/ping" reqUrl  setApiKey =     let apiKey = "123456789XYZ"@@ -191,7 +188,7 @@ testPostConfig endpoint params =      testCase ("Test POST " ++ endpoint) $ do         configSample <- head <$> sample' (arbitrary :: Gen Config)-        withRequest (Post.sendConfig configSample) $ +        withRequest (Post.config configSample) $              \loggedRequest ->                  assertPost loggedRequest endpoint params configSample @@ -202,8 +199,8 @@ requestUnits :: TestTree requestUnits = testGroup "Unit Tests for Requests"      [ testGroup "Basic Tests"-        [ testCase "GET Request"  $ basicRequest GET  "/rest/ping" Get.ping-        , testCase "POST Request" $ basicRequest POST "/rest/ping" Post.ping+        [ testCase "GET Request"  $ basicReq GET  "/rest/system/ping" Get.ping+        , testCase "POST Request" $ basicReq POST "/rest/system/ping" Post.ping         , testCase "changeServer"           changeServer         , testCase "set ApiKey"             setApiKey         , testCase "enable Authentication"  enableAuth@@ -211,76 +208,83 @@         , testCase "disable HTTPS usage"    disableHttps         ]     , testGroup "GET Requests"-        [ testGet "/rest/ping"          noParams Get.ping-        , testGet "/rest/completion"+        [ testGet "/rest/system/ping"           noParams Get.ping+        , testGet "/rest/db/completion"                   [("folder","default"), ("device", "device1")]                    (Get.completion "device1" "default")-        , testGet "/rest/config"        noParams Get.config-        , testGet "/rest/connections"   noParams Get.connections-        , testGet "/rest/deviceid" +        , testGet "/rest/system/config"         noParams Get.config+        , testGet "/rest/system/connections"    noParams Get.connections+        , testGet "/rest/svc/deviceid"                    [("id", "device1")]                    (Get.deviceId "device1")-        , testGet "/rest/discovery"     noParams Get.discovery-        , testGet "/rest/errors"        noParams Get.errors-        , testGet "/rest/ignores" +        , testGet "/rest/system/discovery"      noParams Get.discovery+        , testGet "/rest/system/error"          noParams Get.errors+        , testGet "/rest/db/ignores"                    [("folder", "default")]                    (Get.ignores "default")-        , testGet "/rest/model" +        , testGet "/rest/db/status"                    [("folder", "default")] -                  (Get.model "default")-        , testGet "/rest/need"  +                  (Get.dbStatus "default")+        , testGet "/rest/db/need"                     [("folder", "default")]                    (Get.need "default")-        , testGet "/rest/config/sync"   noParams Get.sync-        , testGet "/rest/report"        noParams Get.report-        , testGet "/rest/system"        noParams Get.system-        , testGet "/rest/tree"        +        , testGet "/rest/system/config/insync"  noParams Get.insync+        , testGet "/rest/svc/report"            noParams Get.report+        , testGet "/rest/system/status"         noParams Get.sysStatus+        , testGet "/rest/db/browse"                           [("folder", "default")]-                  (Get.tree "default" Nothing Nothing)-        , testGet "/rest/tree"        +                  (Get.browse "default" Nothing Nothing)+        , testGet "/rest/db/browse"                           [("folder", "default"), ("prefix", "foo/bar")]-                  (Get.tree "default" (Just "foo/bar") Nothing)-        , testGet "/rest/tree"        +                  (Get.browse "default" (Just "foo/bar") Nothing)+        , testGet "/rest/db/browse"                           [("folder", "default"), ("levels", "2")]-                  (Get.tree "default" Nothing (Just 2))-        , testGet "/rest/tree"        +                  (Get.browse "default" Nothing (Just 2))+        , testGet "/rest/db/browse"                           [("folder", "default"),("prefix", "foo/bar"),("levels", "2")]-                  (Get.tree "default" (Just "foo/bar") (Just 2))-        , testGet "/rest/upgrade"       noParams Get.upgrade-        , testGet "/rest/version"       noParams Get.version+                  (Get.browse "default" (Just "foo/bar") (Just 2))+        , testGet "/rest/db/file"        +                  [("folder", "default"), ("file", "foo/bar")]+                  (Get.file "default" "foo/bar")+        , testGet "/rest/system/upgrade"        noParams Get.upgrade+        , testGet "/rest/system/version"        noParams Get.version+        , testGet "/rest/svc/lang"              noParams Get.lang+        , testGet "/rest/stats/device"          noParams Get.devices+        , testGet "/rest/stats/folder"          noParams Get.folders         ]     , testGroup "POST Requests"-        [ testPost "/rest/ping"         noParams noPayload Post.ping-        , testPost "/rest/bump"+        [ testPost "/rest/system/ping"          noParams noPayload Post.ping+        , testPost "/rest/db/prio"                    [("folder", "default"), ("file", "foo/bar")]                     noPayload-                   (Post.bump "default" "foo/bar")-        , testPost "/rest/discovery/hint"+                   (Post.prio "default" "foo/bar")+        , testPost "/rest/system/discovery"                    [("device", "device1"), ("addr", "192.168.0.10:8080")]                     noPayload-                   (Post.hint "device1" "192.168.0.10:8080")-        , testPost "/rest/error" +                   (Post.discovery "device1" "192.168.0.10:8080")+        , testPost "/rest/system/error"                     noParams                     (T.pack "Error 1")                     (Post.sendError "Error 1")-        , testPost "/rest/error/clear"  noParams noPayload Post.clearErrors-        , testPost "/rest/scan" +        , testPost "/rest/system/error/clear" +                   noParams noPayload Post.clearErrors+        , testPost "/rest/db/scan"                     [("folder", "default")]                     noPayload -                   (Post.scanFolder "default" Nothing)-        , testPost "/rest/scan"+                   (Post.scan "default" Nothing)+        , testPost "/rest/db/scan"                    [("folder", "default"), ("sub", "foo/bar")]                     noPayload -                   (Post.scanFolder "default" (Just "foo/bar"))-        , testPost "/rest/restart"      noParams noPayload Post.restart-        , testPost "/rest/shutdown"     noParams noPayload Post.shutdown-        , testPost "/rest/reset"        noParams noPayload Post.reset-        , testPost "/rest/upgrade"      noParams noPayload Post.upgrade-        , testPost "/rest/ignores" +                   (Post.scan "default" (Just "foo/bar"))+        , testPost "/rest/system/restart"  noParams noPayload Post.restart+        , testPost "/rest/system/shutdown" noParams noPayload Post.shutdown+        , testPost "/rest/system/reset"    noParams noPayload Post.reset+        , testPost "/rest/system/upgrade"  noParams noPayload Post.upgrade+        , testPost "/rest/db/ignores"                     [("folder", "default")]                     (createIgnoresMap ["file1", "file2", "foo/bar"])-                   (Post.sendIgnores "default" ["file1", "file2", "foo/bar"])-        , testPostConfig "/rest/config" noParams+                   (Post.ignores "default" ["file1", "file2", "foo/bar"])+        , testPostConfig "/rest/system/config" noParams         ]     ]