packages feed

net-spider-cli 0.1.0.2 → 0.2.0.0

raw patch · 4 files changed

+155/−55 lines, 4 filesdep ~net-spiderPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: net-spider

API changes (from Hackage documentation)

- NetSpider.CLI.Snapshot: [basisSnapshotQuery] :: SnapshotConfig n na fla sla -> Query n na fla sla
+ NetSpider.CLI.Snapshot: data CLISnapshotQuery n
+ NetSpider.CLI.Snapshot: instance GHC.Classes.Eq n => GHC.Classes.Eq (NetSpider.CLI.Snapshot.CLISnapshotQuery n)
+ NetSpider.CLI.Snapshot: instance GHC.Classes.Ord n => GHC.Classes.Ord (NetSpider.CLI.Snapshot.CLISnapshotQuery n)
+ NetSpider.CLI.Snapshot: instance GHC.Show.Show n => GHC.Show.Show (NetSpider.CLI.Snapshot.CLISnapshotQuery n)
+ NetSpider.CLI.Snapshot: makeSnapshotQuery :: Query n na fla sla -> CLISnapshotQuery n -> Either String (Query n na fla sla)
- NetSpider.CLI.Snapshot: SnapshotConfig :: ReadM n -> Query n na fla sla -> Bool -> SnapshotConfig n na fla sla
+ NetSpider.CLI.Snapshot: SnapshotConfig :: ReadM n -> Bool -> SnapshotConfig n
- NetSpider.CLI.Snapshot: [nodeIDReader] :: SnapshotConfig n na fla sla -> ReadM n
+ NetSpider.CLI.Snapshot: [nodeIDReader] :: SnapshotConfig n -> ReadM n
- NetSpider.CLI.Snapshot: [startsFromAsArguments] :: SnapshotConfig n na fla sla -> Bool
+ NetSpider.CLI.Snapshot: [startsFromAsArguments] :: SnapshotConfig n -> Bool
- NetSpider.CLI.Snapshot: data SnapshotConfig n na fla sla
+ NetSpider.CLI.Snapshot: data SnapshotConfig n
- NetSpider.CLI.Snapshot: parserSnapshotQuery :: SnapshotConfig n na fla sla -> Parser (Query n na fla sla)
+ NetSpider.CLI.Snapshot: parserSnapshotQuery :: SnapshotConfig n -> Parser (CLISnapshotQuery n)

Files

ChangeLog.md view
@@ -1,5 +1,15 @@ # Revision history for net-spider-cli +## 0.2.0.0  -- 2019-10-13++* Add `--duration` option to `parserSnapshotQuery` function.+* [BREAKING CHANGE] `parserSnapshotQuery` function now returns a new type `CLISnapshotQuery`.+  This is because some combinations of `--time-from`, `--time-to` and `--duration` options+  are invalid. To express that case, users have to use `makeSnapshotQuery` function to get+  a `Query` object, and that function can fail.+* [BREAKING CHANGE] Remove `basisSnapshotQuery` field from `SnapshotConfig`.+  `makeSnapshotQuery` function now plays the same role.+ ## 0.1.0.2  -- 2019-10-04  * Confirm test with `hashable-1.3.0.0`.
net-spider-cli.cabal view
@@ -1,5 +1,5 @@ name:                   net-spider-cli-version:                0.1.0.2+version:                0.2.0.0 author:                 Toshio Ito <debug.ito@gmail.com> maintainer:             Toshio Ito <debug.ito@gmail.com> license:                BSD3@@ -24,7 +24,7 @@                         NetSpider.CLI.Snapshot   -- other-modules:           build-depends:        base >=4.11.1.0 && <4.13,-                        net-spider >=0.3.2.0 && <0.4,+                        net-spider >=0.3.3.0 && <0.4,                         optparse-applicative >=0.14.3.0 && <0.16,                         aeson >=1.2.4 && <1.5,                         hashable >=1.2.6.1 && <1.4,
src/NetSpider/CLI/Snapshot.hs view
@@ -7,22 +7,29 @@ -- graphs. module NetSpider.CLI.Snapshot   ( parserSnapshotQuery,-    SnapshotConfig(..)+    makeSnapshotQuery,+    SnapshotConfig(..),+    CLISnapshotQuery   ) where -import Control.Applicative ((<$>), (<*>), many)+import Control.Applicative ((<$>), (<*>), (<|>), many, optional, empty)+import Data.Int (Int64)+import NetSpider.Interval+  ( interval, parseTimeIntervalEnd, secSince, secUntil,+    IntervalEnd, Interval+  ) import qualified NetSpider.Query as Q+import NetSpider.Timestamp (Timestamp) import qualified Options.Applicative as Opt-import NetSpider.Interval (interval, parseTimeIntervalEnd)+import qualified Options.Applicative.Types as OptT  -- | Configuration for option parser for Snapshot 'Q.Query'.-data SnapshotConfig n na fla sla =+--+-- @since 0.2.0.0+data SnapshotConfig n =   SnapshotConfig   { nodeIDReader :: Opt.ReadM n,     -- ^ Parser that reads a CLI option to generate a node ID.-    basisSnapshotQuery :: Q.Query n na fla sla,-    -- ^ Basis for queries for snapshot graphs. Fields in this basis-    -- are overwritten by CLI options.     startsFromAsArguments :: Bool     -- ^ If 'True', the 'Q.startsFrom' field is read from CLI     -- arguments. If 'False', arguments are not parsed. In either@@ -30,14 +37,60 @@     -- 'Q.startsFrom'.   } --- | CLI option parser for 'Q.Query'.-parserSnapshotQuery :: SnapshotConfig n na fla sla-                    -> Opt.Parser (Q.Query n na fla sla)-parserSnapshotQuery conf = fmap fromParsedElement the_parser+-- | Settings for Snapshot 'Q.Query' parsed from the command-line+-- options. You can make 'Q.Query' by 'makeSnapshotQuery' function.+--+-- @since 0.2.0.0+data CLISnapshotQuery n =+  CLISnapshotQuery+  { startsFrom :: [n],+    timeDurationSec :: Maybe Int64,+    timeFrom :: Maybe (IntervalEnd Timestamp),+    timeTo :: Maybe (IntervalEnd Timestamp)+  }+  deriving (Show,Eq,Ord)++-- | Make a 'Q.Query' by applying 'CLISnapshotQuery' to the base+-- query. The 'CLISnapshotQuery' overwrites 'Q.startsFrom' and+-- 'Q.timeInterval' fields.+--+-- It can fail to convert 'CLISnapshotQuery' to 'Q.Query' fields. In+-- that case, the result is 'Left' with a human-readable error+-- message.+--+-- @since 0.2.0.0+makeSnapshotQuery :: Q.Query n na fla sla -- ^ base query+                  -> CLISnapshotQuery n+                  -> Either String (Q.Query n na fla sla)+                  -- ^ Left: human-readable error message. Right: updated query+makeSnapshotQuery q cliq = do+  ivl <- makeTimeInterval cliq+  return $ q { Q.startsFrom = startsFrom cliq,+               Q.timeInterval = ivl+             }++makeTimeInterval :: CLISnapshotQuery n -> Either String (Interval Timestamp)+makeTimeInterval c =+  case (timeFrom c, timeTo c, timeDurationSec c) of+    (ms, me, Nothing) -> Right $ interval s e+      where+        s = maybe (Q.NegInf, False) id ms+        e = maybe (Q.PosInf, False) id me+    (Just s, Nothing, Just d) -> Right $ secSince d s+    (Nothing, Just e, Just d) -> Right $ secUntil d e+    (Just _, Just _, Just _) -> Left ("Specifying all --time-to, --time-from and --duration is not allowed.")+    (Nothing, Nothing, Just _) -> Left ("Specifying --duration only is not allowed. Specify --time-to or --time-from, too.")++-- | CLI option parser for Snapshot 'Q.Query'. Use 'makeSnapshotQuery'+-- to convert 'CLISnapshotQuery' to 'Q.Query'.+--+-- @since 0.2.0.0+parserSnapshotQuery :: SnapshotConfig n+                    -> Opt.Parser (CLISnapshotQuery n)+parserSnapshotQuery conf =+  CLISnapshotQuery <$> pStartsFromTotal <*> pDuration <*> pTimeLower <*> pTimeUpper   where-    basis = basisSnapshotQuery conf-    fromParsedElement (sf, ti) = basis { Q.startsFrom = sf, Q.timeInterval = ti }-    the_parser = (,) <$> ((++) <$> pStartsFrom <*> pStartsFromArgs) <*> pTimeInterval+    pStartsFromTotal = (++) <$> pStartsFrom <*> pStartsFromArgs     rNodeID = nodeIDReader conf     nodeID_metavar = "NODE-ID"     pStartsFrom = many $ Opt.option rNodeID $ mconcat@@ -52,30 +105,41 @@                            [ Opt.help $ "Same as -s option.",                              Opt.metavar $ nodeID_metavar                            ]-    pTimeInterval = interval <$> pTimeLower <*> pTimeUpper-    pTimeLower = Opt.option (Opt.eitherReader parseTimeIntervalEnd) $ mconcat-                 [ Opt.short 'f',-                   Opt.long "time-from",-                   Opt.help ( "Lower bound of query timestamp. "-                              ++ "Local findings with timestamp newer than this value are used to create the snapshot graph. "-                              ++ "ISO 8601 format is used for timestamps (e.g. `2019-03-22T10:20:12+09:00`). "-                              ++ "The timezone is optional. "-                              ++ "By default, the lower bound is inclusive. "-                              ++ "Add prefix 'x' to make it exclusive (e.g. `x2019-03-22T10:20:12+09:00`). "-                              ++ "Prefix of 'i' explicitly mark the lower bound is inclusive. "-                              ++ "Special value `x-inf` indicates there is no lower bound. " -                              ++ "Default: x-inf"),-                   Opt.metavar "TIMESTAMP",-                   Opt.value (Q.NegInf, False)-                 ]-    pTimeUpper = Opt.option (Opt.eitherReader parseTimeIntervalEnd) $ mconcat-                 [ Opt.short 't',-                   Opt.long "time-to",-                   Opt.help ( "Upper bound of query timestamp. "-                              ++ "Local findings with timestamp older than this value are used to create the snapshot graph. "-                              ++ "See --time-from for format of timestamps. "-                              ++ "Special value `x+inf` indicates there is no upper bound. " -                              ++ "Default: x+inf"),-                   Opt.metavar "TIMESTAMP",-                   Opt.value (Q.PosInf, False)-                 ]+    pTimeLower =+      optional $ Opt.option (Opt.eitherReader parseTimeIntervalEnd) $ mconcat+      ( [ Opt.short 'f',+          Opt.long "time-from",+          Opt.help ( "Lower bound of query timestamp. "+                     ++ "Local findings with timestamp newer than this value are used to create the snapshot graph. "+                     ++ "ISO 8601 format is used for timestamps (e.g. `2019-03-22T10:20:12+09:00`). "+                     ++ "The timezone is optional. "+                     ++ "By default, the lower bound is inclusive. "+                     ++ "Add prefix 'x' to make it exclusive (e.g. `x2019-03-22T10:20:12+09:00`). "+                     ++ "Prefix of 'i' explicitly mark the lower bound is inclusive. "+                     ++ "Special value `x-inf` indicates there is no lower bound. "+                     ++ "Default: x-inf"+                   ),+          Opt.metavar "TIMESTAMP"+        ]+      )+    pTimeUpper =+      optional $ Opt.option (Opt.eitherReader parseTimeIntervalEnd) $ mconcat+      ( [ Opt.short 't',+          Opt.long "time-to",+          Opt.help ( "Upper bound of query timestamp. "+                     ++ "Local findings with timestamp older than this value are used to create the snapshot graph. "+                     ++ "See --time-from for format of timestamps. "+                     ++ "Special value `x+inf` indicates there is no upper bound. "+                     ++ "Default: x+inf"+                   ),+          Opt.metavar "TIMESTAMP"+        ]+      )+    pDuration = optional $ Opt.option Opt.auto $ mconcat+                [ Opt.short 'd',+                  Opt.long "duration",+                  Opt.help ( "Duration of the query time interval in seconds. "+                             ++ " Use with either --time-to or --time-from option."+                           ),+                  Opt.metavar "SECONDS"+                ]
test/NetSpider/CLI/SnapshotSpec.hs view
@@ -1,68 +1,94 @@ module NetSpider.CLI.SnapshotSpec (main,spec) where +import Data.List (isInfixOf) import NetSpider.Interval (interval, Extended(..)) import NetSpider.Query   ( startsFrom, defQuery, timeInterval,-    foundNodePolicy, policyAppend+    foundNodePolicy, policyAppend,+    Query   ) import NetSpider.Timestamp (fromEpochMillisecond) import qualified Options.Applicative as Opt import Test.Hspec  import NetSpider.CLI.TestCommon (runP)-import NetSpider.CLI.Snapshot (SnapshotConfig(..), parserSnapshotQuery)+import NetSpider.CLI.Snapshot (SnapshotConfig(..), parserSnapshotQuery, makeSnapshotQuery) -defConfig :: SnapshotConfig Int () () ()+defConfig :: SnapshotConfig Int defConfig =   SnapshotConfig   { nodeIDReader = Opt.auto,-    basisSnapshotQuery = (defQuery []) { foundNodePolicy = policyAppend },     startsFromAsArguments = False   }  main :: IO () main = hspec spec +parseSQ :: SnapshotConfig Int -> [String] -> Either String (Query Int () () ())+parseSQ sconf args = makeSnapshotQuery base_query =<< runP (parserSnapshotQuery sconf) args+  where+    base_query = (defQuery []) { foundNodePolicy = policyAppend }+ spec :: Spec spec = describe "parserSnapshotQuery" $ do   specify "default" $ do-    let (Right got) = runP (parserSnapshotQuery defConfig) []+    let (Right got) = parseSQ defConfig []     startsFrom got `shouldBe` []     timeInterval got `shouldBe` interval (NegInf, False) (PosInf, False)     foundNodePolicy got `shouldBe` policyAppend   specify "time-from" $ do-    let (Right got) = runP (parserSnapshotQuery defConfig)+    let (Right got) = parseSQ defConfig                       ["--time-from", "2019-02-19T11:12:00"]     timeInterval got `shouldBe`       interval (Finite $ fromEpochMillisecond 1550574720000, True)                (PosInf, False)   specify "time-to with exclusive" $ do-    let (Right got) = runP (parserSnapshotQuery defConfig)+    let (Right got) = parseSQ defConfig                       ["--time-to", "x2017-12-20T19:22:02"]     timeInterval got `shouldBe`       interval (NegInf, False)                (Finite $ fromEpochMillisecond 1513797722000, False)   specify "both time-from and time-to with inclusive" $ do-    let (Right got) = runP (parserSnapshotQuery defConfig)+    let (Right got) = parseSQ defConfig                       ["-f", "i2018-10-11T14:13:33", "-t", "i2018-10-11T14:13:50.332"]     timeInterval got `shouldBe`       interval (Finite $ fromEpochMillisecond 1539267213000, True)                (Finite $ fromEpochMillisecond 1539267230332, True)   specify "explicit infinity" $ do-    let (Right got) = runP (parserSnapshotQuery defConfig)+    let (Right got) = parseSQ defConfig                       ["--time-from", "-inf", "--time-to", "+inf"]     timeInterval got `shouldBe` interval (NegInf, True) (PosInf, True)   specify "multiple starts-from" $ do-    let (Right got) = runP (parserSnapshotQuery defConfig)+    let (Right got) = parseSQ defConfig                       ["-s", "10", "-s", "12", "-s", "15"]     startsFrom got `shouldBe` [10,12,15]   let argsConfig = defConfig { startsFromAsArguments = True }   specify "startsFromAsArguments" $ do-    let (Right got) = runP (parserSnapshotQuery argsConfig)+    let (Right got) = parseSQ argsConfig                       ["143", "200", "473","21"]     startsFrom got `shouldBe` [143, 200, 473, 21]   specify "startsFromAsArguments - -s still enabled" $ do-    let (Right got) = runP (parserSnapshotQuery argsConfig)+    let (Right got) = parseSQ argsConfig                       ["90", "-s", "181"]     startsFrom got `shouldBe` [181, 90]+  specify "duration + time-from" $ do+    let (Right got) = parseSQ defConfig+                      ["--duration", "3600", "--time-from", "i2019-04-30T19:03:33"]+    timeInterval got `shouldBe`+      interval (Finite $ fromEpochMillisecond 1556651013000, True)+               (Finite $ fromEpochMillisecond (1556651013000 + 3600000), False)+  specify "duration + time-to" $ do+    let (Right got) = parseSQ defConfig+                      ["-d", "600", "--time-to", "x2019-04-30T19:03:33"]+    timeInterval got `shouldBe`+      interval (Finite $ fromEpochMillisecond (1556651013000 - 600000), True)+               (Finite $ fromEpochMillisecond 1556651013000, False)+  specify "duration + time-to + time-from expects error" $ do+    let (Left err) = parseSQ defConfig+                     ["-d", "600", "--time-to", "x2019-04-30T19:03:33", "--time-from", "x2019-04-30T17:00:52"]+    err `shouldSatisfy` (isInfixOf "all --time-to, --time-from and --duration is not allowed")+  specify "duration without time-to or time-from" $ do+    let (Left err) = parseSQ defConfig+                     ["-d", "600"]+    err `shouldSatisfy` (isInfixOf "--duration only is not allowed")