Skip to content

Commit 422e1bf

Browse files
authored
Merge pull request #83 from vst/67-revisit-types-for-further-type-safety-and-better-schema-output
Fix Invalic Date/Time JSON Schema Output
2 parents 9d846ac + 002a869 commit 422e1bf

File tree

4 files changed

+83
-8
lines changed

4 files changed

+83
-8
lines changed

src/HostPatrol/Remote.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import qualified Data.List as List
1919
import Data.Maybe (fromMaybe)
2020
import qualified Data.Scientific as S
2121
import qualified Data.Text as T
22-
import qualified Data.Time as Time
2322
import qualified HostPatrol.Config as Config
2423
import qualified HostPatrol.Meta as Meta
2524
import qualified HostPatrol.Types as Types
@@ -29,6 +28,7 @@ import qualified System.Process.Typed as TP
2928
import Text.Read (readEither)
3029
import qualified Zamazingo.Ssh as Z.Ssh
3130
import qualified Zamazingo.Text as Z.Text
31+
import qualified Zamazingo.Time as Z.Time
3232

3333

3434
-- * Report
@@ -42,7 +42,7 @@ compileReport
4242
-> Config.Config
4343
-> m Types.Report
4444
compileReport par Config.Config {..} = do
45-
now <- liftIO Time.getCurrentTime
45+
now <- Z.Time.getNow
4646
(errs, _reportHosts) <- liftIO (compileHostReportsIO par _configHosts)
4747
_reportKnownSshKeys <- concat <$> mapM parseSshPublicKeys _configKnownSshKeys
4848
let _reportMeta =
@@ -367,7 +367,7 @@ _jsonDecoderDockerContainer =
367367
<$> ACD.key "Id" ACD.text
368368
<*> (T.dropWhile (== '/') <$> ACD.key "Name" ACD.text)
369369
<*> ACD.at ["Config", "Image"] ACD.text
370-
<*> ACD.key "Created" ACD.utcTime
370+
<*> ACD.key "Created" ACD.auto
371371
<*> ((==) True <$> ACD.at ["State", "Running"] ACD.bool)
372372

373373

src/HostPatrol/Types.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import qualified Data.Aeson as Aeson
1111
import Data.Int (Int32)
1212
import Data.Scientific (Scientific)
1313
import qualified Data.Text as T
14-
import qualified Data.Time as Time
1514
import GHC.Generics (Generic)
1615
import Zamazingo.Ssh (SshConfig)
16+
import qualified Zamazingo.Time as Z.Time
1717

1818

1919
-- * Report
@@ -51,7 +51,7 @@ data ReportMeta = ReportMeta
5151
{ _reportMetaVersion :: !T.Text
5252
, _reportMetaBuildTag :: !(Maybe T.Text)
5353
, _reportMetaBuildHash :: !(Maybe T.Text)
54-
, _reportMetaTimestamp :: !Time.UTCTime
54+
, _reportMetaTimestamp :: !Z.Time.DateTime
5555
}
5656
deriving (Eq, Generic, Show)
5757
deriving (Aeson.FromJSON, Aeson.ToJSON) via (ADC.Autodocodec ReportMeta)
@@ -303,7 +303,7 @@ data DockerContainer = DockerContainer
303303
{ _dockerContainerId :: !T.Text
304304
, _dockerContainerName :: !T.Text
305305
, _dockerContainerImage :: !T.Text
306-
, _dockerContainerCreated :: !Time.UTCTime
306+
, _dockerContainerCreated :: !Z.Time.DateTime
307307
, _dockerContainerRunning :: !Bool
308308
}
309309
deriving (Eq, Generic, Show)

src/Zamazingo/Time.hs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
{-# LANGUAGE DeriveGeneric #-}
2+
{-# LANGUAGE DerivingVia #-}
3+
{-# LANGUAGE OverloadedStrings #-}
4+
5+
-- | This module provides auxiliary definitions for working with
6+
-- date/time values.
7+
module Zamazingo.Time where
8+
9+
import qualified Autodocodec as ADC
10+
import Control.Monad.IO.Class (MonadIO (liftIO))
11+
import qualified Data.Aeson as Aeson
12+
import qualified Data.Text as T
13+
import qualified Data.Time as Time
14+
import qualified Data.Time.Format.ISO8601 as Time.Iso8601
15+
import GHC.Generics (Generic)
16+
17+
18+
-- | Data definition for date/time information.
19+
--
20+
-- The reason that this type exists is to avoid the legacy
21+
-- 'Time.UTCTime' type schema typing.
22+
newtype DateTime = DateTime
23+
{ _unDateTime :: Time.UTCTime
24+
}
25+
deriving (Eq, Generic, Show)
26+
deriving (Aeson.FromJSON, Aeson.ToJSON) via (ADC.Autodocodec DateTime)
27+
28+
29+
-- | 'ADC.HasCodec' instance for 'DateTime'.
30+
--
31+
-- >>> :set -XTypeApplications
32+
-- >>> import Autodocodec as ADC
33+
-- >>> import Autodocodec.Schema as ADC.Schema
34+
--
35+
-- >>> let now = DateTime (read "2021-09-01 12:00:00 UTC" :: Time.UTCTime)
36+
-- >>> Aeson.encode now
37+
-- "\"2021-09-01T12:00:00Z\""
38+
-- >>> Aeson.decode @DateTime (Aeson.encode now)
39+
-- Just (DateTime {_unDateTime = 2021-09-01 12:00:00 UTC})
40+
--
41+
-- >>> Aeson.encode (ADC.Schema.jsonSchemaViaCodec @DateTime)
42+
-- "{\"$comment\":\"Date/time in ISO8601 format.\",\"type\":\"string\"}"
43+
instance ADC.HasCodec DateTime where
44+
codec =
45+
ADC.bimapCodec (fmap DateTime . parseIso8601) (iso8601 . _unDateTime) ADC.textCodec ADC.<?> "Date/time in ISO8601 format."
46+
47+
48+
-- | Returns current date/time information.
49+
--
50+
-- > getNow
51+
-- DateTime {_unDateTime = 2024-04-18 00:48:15.956143715 UTC}
52+
getNow :: MonadIO m => m DateTime
53+
getNow = liftIO (DateTime <$> Time.getCurrentTime)
54+
55+
56+
-- | Parses ISO8601 date/time string.
57+
parseIso8601 :: T.Text -> Either String Time.UTCTime
58+
parseIso8601 t =
59+
maybe (Left err) pure (Time.Iso8601.formatParseM Time.Iso8601.iso8601Format (T.unpack t))
60+
where
61+
err = "ISO8601 date/time parse error: " <> show t
62+
63+
64+
-- | Parses ISO8601 date/time string in 'MonadFail' context.
65+
parseIso8601M :: MonadFail m => T.Text -> m Time.UTCTime
66+
parseIso8601M =
67+
either fail pure . parseIso8601
68+
69+
70+
-- | Formats 'Time.UTCTime' into an ISO8601 date/time string.
71+
iso8601 :: Time.UTCTime -> T.Text
72+
iso8601 = T.pack . Time.Iso8601.formatShow Time.Iso8601.iso8601Format

website/src/lib/data.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,10 @@ export const HOSTPATROL_REPORT_SCHEMA = {
119119
items: {
120120
$comment: 'Docker Container Information\nDockerContainer',
121121
properties: {
122-
created: { $comment: 'Date/time when the container is created at.\nLocalTime', type: 'string' },
122+
created: {
123+
$comment: 'Date/time when the container is created at.\nDate/time in ISO8601 format.',
124+
type: 'string',
125+
},
123126
id: { $comment: 'ID of the container..', type: 'string' },
124127
image: { $comment: 'Image the container is created from.', type: 'string' },
125128
name: { $comment: 'Name of the container.', type: 'string' },
@@ -275,7 +278,7 @@ export const HOSTPATROL_REPORT_SCHEMA = {
275278
properties: {
276279
buildHash: { $comment: 'Build hash of the application.', type: 'string' },
277280
buildTag: { $comment: 'Build tag of the application.', type: 'string' },
278-
timestamp: { $comment: 'Timestamp of the report.\nLocalTime', type: 'string' },
281+
timestamp: { $comment: 'Timestamp of the report.\nDate/time in ISO8601 format.', type: 'string' },
279282
version: { $comment: 'Version of the application.', type: 'string' },
280283
},
281284
required: ['timestamp', 'version'],

0 commit comments

Comments
 (0)