@@ -3,6 +3,8 @@ module OpenAPI.Checker.Report
3 , ReportInput (..)
3 , ReportInput (..)
4 , ReportStatus (..)
4 , ReportStatus (..)
7+ , ReportTreeStyle (..)
@@ -10,6 +12,7 @@ import Control.Monad.Free hiding (unfoldM)
10import Control.Monad.Reader
12import Control.Monad.Reader
11import Control.Monad.Writer
13import Control.Monad.Writer
12import Data.Aeson (ToJSON)
14import Data.Aeson (ToJSON)
13import Data.Either
16import Data.Either
14import Data.Foldable
17import Data.Foldable
15import Data.Function
18import Data.Function
@@ -54,8 +57,20 @@ data ReportStatus
54 -- there actually are any breaking changes.
57 -- there actually are any breaking changes.
55 OnlyUnsupportedChanges
58 OnlyUnsupportedChanges
60+data ReportConfig = ReportConfig
61+ { treeStyle :: ReportTreeStyle
64+instance Default ReportConfig where
67+ { treeStyle = HeadersTreeStyle
70+data ReportTreeStyle = HeadersTreeStyle | FoldingBlockquotesTreeStyle
57−generateReport :: ReportInput -> (Pandoc, ReportStatus)
72+generateReport :: ReportConfig -> ReportInput -> (Pandoc, ReportStatus)
58−generateReport inp =
73+generateReport cfg inp =
59 let partitionUnsupported = P.partition (\(AnIssue _ i) -> issueIsUnsupported i)
74 let partitionUnsupported = P.partition (\(AnIssue _ i) -> issueIsUnsupported i)
60 (bUnsupported, breaking) = partitionUnsupported $ breakingChanges inp
75 (bUnsupported, breaking) = partitionUnsupported $ breakingChanges inp
61 (nbUnsupported, invertIssueOrientationP -> nonBreaking) = partitionUnsupported $ nonBreakingChanges inp
76 (nbUnsupported, invertIssueOrientationP -> nonBreaking) = partitionUnsupported $ nonBreakingChanges inp
@@ -64,8 +79,8 @@ generateReport inp =
64 nonBreakingChangesPresent = not $ P.null nonBreaking
79 nonBreakingChangesPresent = not $ P.null nonBreaking
65 unsupportedChangesPresent = not $ P.null unsupported
80 unsupportedChangesPresent = not $ P.null unsupported
66 report = doc $
81 report = doc $
67− runReportMonad jets $ do
82+ runReportMonad cfg jets $ do
68− smartHeader "Summary"
83+ tell $ header 1 "Summary"
70 simpleTable
85 simpleTable
@@ -75,13 +90,13 @@ generateReport inp =
76 [para . show' <$> [P.size breaking, P.size nonBreaking, P.size unsupported]]
91 [para . show' <$> [P.size breaking, P.size nonBreaking, P.size unsupported]]
77 when breakingChangesPresent $ do
92 when breakingChangesPresent $ do
78− smartHeader $ anchor breakingChangesId <> "⚠️ Breaking changes"
93+ tell $ header 1 $ anchor breakingChangesId <> "⚠️ Breaking changes"
79 incrementHeaders $ showErrs breaking
94 incrementHeaders $ showErrs breaking
80 when nonBreakingChangesPresent $ do
95 when nonBreakingChangesPresent $ do
81− smartHeader $ anchor nonBreakingChangesId <> "🙆 Non-breaking changes"
96+ tell $ header 1 $ anchor nonBreakingChangesId <> "🙆 Non-breaking changes"
82 incrementHeaders $ showErrs nonBreaking
97 incrementHeaders $ showErrs nonBreaking
83 when unsupportedChangesPresent $ do
98 when unsupportedChangesPresent $ do
84− smartHeader $ anchor unsupportedChangesId <> "🤷 Unsupported feature changes"
99+ tell $ header 1 $ anchor unsupportedChangesId <> "🤷 Unsupported feature changes"
85 incrementHeaders $ showErrs unsupported
100 incrementHeaders $ showErrs unsupported
@@ -105,24 +120,45 @@ generateReport inp =
105data ReportState = ReportState
120data ReportState = ReportState
106 { sourceJets :: [ReportJet' Behave Inlines]
121 { sourceJets :: [ReportJet' Behave Inlines]
107 , headerLevel :: Int
122 , headerLevel :: Int
123+ , headerHandle :: Inlines -> ReportMonad () -> ReportMonad ()
124+ , incrementHeadersHandle :: ReportState -> ReportState
110type ReportMonad = ReaderT ReportState (Writer Blocks)
127type ReportMonad = ReaderT ReportState (Writer Blocks)
112−runReportMonad :: [ReportJet' Behave Inlines] -> ReportMonad () -> Blocks
129+runReportMonad :: ReportConfig -> [ReportJet' Behave Inlines] -> ReportMonad () -> Blocks
113−runReportMonad jts =
130+runReportMonad cfg jts =
114 execWriter
131 execWriter
116 runReaderT
133 runReaderT
117 ReportState
134 ReportState
118 { sourceJets = jts
135 { sourceJets = jts
119 , headerLevel = 1
136 , headerLevel = 1
138+ case treeStyle cfg of
139+ HeadersTreeStyle -> \i body -> do
140+ h <- asks headerLevel
143+ FoldingBlockquotesTreeStyle -> \i body -> do
146+ <> rawHtml "<summary>"
148+ <> rawHtml "</summary>"
149+ (mapReaderT . mapWriterT . fmap . fmap) blockQuote $ body
150+ tell $ rawHtml "</details>"
152+ rawHtml = rawBlock "html"
153+ , incrementHeadersHandle = case treeStyle cfg of
154+ HeadersTreeStyle -> \l -> l {headerLevel = headerLevel l + 1}
155+ FoldingBlockquotesTreeStyle -> id
122−smartHeader :: Inlines -> ReportMonad ()
158+smartHeader :: Inlines -> ReportMonad () -> ReportMonad ()
123−smartHeader i = do
159+smartHeader i body = do
124− h <- asks headerLevel
160+ f <- asks headerHandle
125− tell $ header h i
161+ f i body
127showErrs :: forall a. Typeable a => P.PathsPrefixTree Behave AnIssue a -> ReportMonad ()
163showErrs :: forall a. Typeable a => P.PathsPrefixTree Behave AnIssue a -> ReportMonad ()
128showErrs x@(P.PathsPrefixNode currentIssues _) = do
164showErrs x@(P.PathsPrefixNode currentIssues _) = do
@@ -141,24 +177,27 @@ showErrs x@(P.PathsPrefixNode currentIssues _) = do
141 in (Just (ori, p'), o)
177 in (Just (ori, p'), o)
142 _ -> (Nothing, currentIssues)
178 _ -> (Nothing, currentIssues)
143 jts <- asks sourceJets
179 jts <- asks sourceJets
180+ tell $ case S.toList otherIssues of
181+ [AnIssue ori i] -> describeIssue ori i
144− for_ otherIssues $ \(AnIssue ori i) -> tell . describeIssue ori $ i
182+ ii -> orderedList $ ii <&> (\(AnIssue ori i) -> describeIssue ori i)
145 case removedPaths of
183 case removedPaths of
146 Just (ori, paths) -> do
184 Just (ori, paths) -> do
147− smartHeader $ case ori of
185+ smartHeader
148− Forward -> "Removed paths"
187+ Forward -> "Removed paths"
149− Backward -> "Added paths"
188+ Backward -> "Added paths")
151− bulletList $
190+ bulletList $
152− paths <&> \case
191+ paths <&> \case
153− (NoPathsMatched p) -> para . code $ T.pack p
192+ (NoPathsMatched p) -> para . code $ T.pack p
154− (AllPathsFailed p) -> para . code $ T.pack p
193+ (AllPathsFailed p) -> para . code $ T.pack p
155 Nothing -> pure ()
194 Nothing -> pure ()
156 unfoldM x (observeJetShowErrs <$> jts) $ \(P.PathsPrefixNode _ subIssues) -> do
195 unfoldM x (observeJetShowErrs <$> jts) $ \(P.PathsPrefixNode _ subIssues) -> do
157 for_ subIssues $ \(WrapTypeable (AStep m)) ->
196 for_ subIssues $ \(WrapTypeable (AStep m)) ->
158 for_ (M.toList m) $ \(bhv, subErrors) -> do
197 for_ (M.toList m) $ \(bhv, subErrors) -> do
159− unless (P.null subErrors) $ do
198+ unless (P.null subErrors) $
160− smartHeader $ describeBehaviour bhv
199+ smartHeader (describeBehaviour bhv) $
161− incrementHeaders $ showErrs subErrors
200+ incrementHeaders $ showErrs subErrors
163unfoldM :: Monad m => a -> [a -> m a] -> (a -> m ()) -> m ()
202unfoldM :: Monad m => a -> [a -> m a] -> (a -> m ()) -> m ()
164unfoldM a [] g = g a
203unfoldM a [] g = g a
@@ -168,8 +207,8 @@ unfoldM a (f : ff) g = do
169incrementHeaders :: ReportMonad x -> ReportMonad x
208incrementHeaders :: ReportMonad x -> ReportMonad x
170incrementHeaders m = do
209incrementHeaders m = do
171− l <- asks headerLevel
210+ f <- asks incrementHeadersHandle
172− local (\x -> x {headerLevel = l + 1}) m
211+ local f m
174jets :: [ReportJet' Behave Inlines]
213jets :: [ReportJet' Behave Inlines]
@@ -241,8 +280,7 @@ observeJetShowErrs' (ReportJet jet) (P.PathsPrefixNode currentIssues subIssues)
241 Free jet' -> fmap (embed $ step bhv) <$> observeJetShowErrs' jet' subErrs
280 Free jet' -> fmap (embed $ step bhv) <$> observeJetShowErrs' jet' subErrs
242 Pure h -> Just $ do
281 Pure h -> Just $ do
243 unless (P.null subErrs) $ do
282 unless (P.null subErrs) $ do
245− incrementHeaders $ showErrs subErrs
283+ smartHeader h $ incrementHeaders $ showErrs subErrs
246 return mempty)
284 return mempty)
247 in (fmap . fmap) (PathsPrefixNode currentIssues mempty <>) $
285 in (fmap . fmap) (PathsPrefixNode currentIssues mempty <>) $
248 if any isRight results
286 if any isRight results