diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e6aa28d9..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "awsm-css"] - path = awsm-css - url = https://github.com/igoradamenko/awsm.css.git diff --git a/app/Data/OpenApi/Compare/Options.hs b/app/Data/OpenApi/Compare/Options.hs index 9b0ea11a..2f373b2d 100644 --- a/app/Data/OpenApi/Compare/Options.hs +++ b/app/Data/OpenApi/Compare/Options.hs @@ -9,6 +9,7 @@ import Data.OpenApi.Compare.Report import GHC.Generics (Generic) import Options.Applicative import Options.Applicative.Help hiding (fullDesc) +import Text.PrettyPrint.ANSI.Leijen ((<$$>),string) parseOptions :: IO Options parseOptions = customExecParser (prefs $ showHelpOnError) optionsParserInfo diff --git a/app/Main.hs b/app/Main.hs index 866698d9..f1bfc251 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -2,6 +2,7 @@ module Main (main) where import Control.Monad import Control.Monad.Except +import Control.Monad.Trans (lift) import Data.Aeson import qualified Data.ByteString.Lazy as BSL import Data.Default diff --git a/awsm-css b/awsm-css deleted file mode 160000 index ad03dcf2..00000000 --- a/awsm-css +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ad03dcf22f6348d9c179cc5d0f82fe5d6b5961d4 diff --git a/compaREST.cabal b/compaREST.cabal index 02c7910c..bb47f59d 100644 --- a/compaREST.cabal +++ b/compaREST.cabal @@ -12,8 +12,8 @@ category: Web build-type: Simple extra-doc-files: README.md CHANGELOG.md -tested-with: GHC == 8.10.4 -extra-source-files: awsm-css/dist/awsm.min.css +tested-with: GHC == 9.6 +extra-source-files: css/awsm.min.css extra-source-files: test/golden/**/*.yaml test/golden/**/*.md @@ -26,13 +26,14 @@ common common-options -Wno-safe -Wno-prepositive-qualified-module -Wno-missing-import-lists + -Wno-missing-kind-signatures -Wno-partial-fields -Wno-all-missed-specialisations -Wno-missing-local-signatures -Wno-unsafe -fconstraint-solver-iterations=0 default-language: Haskell2010 - build-depends: base >= 4.12.0.0 && < 4.16 + build-depends: base >= 4.12.0.0 && < 5 , text @@ -165,6 +166,7 @@ executable compaREST , containers , doctemplates , pandoc-types + , prettyprinter-compat-ansi-wl-pprint ghc-options: -threaded -rtsopts -with-rtsopts=-N diff --git a/css/awsm.min.css b/css/awsm.min.css new file mode 100644 index 00000000..ecf39e82 --- /dev/null +++ b/css/awsm.min.css @@ -0,0 +1 @@ +@charset "UTF-8";html{font-family:"PT Serif",Georgia,serif;font-size:100%;line-height:1.6;background:#fff;color:#000;-webkit-overflow-scrolling:touch}body{margin:1em;font-size:1rem}@media (min-width:20rem){body{font-size:calc(1rem + .0125*(100vw - 20rem))}}@media (min-width:40rem){body{font-size:1.25rem}}body article,body footer,body header,body main{position:relative;max-width:40rem;margin:0 auto}body>header{margin-bottom:3.5em}body>header h1{margin:0;font-size:1.5em}body>header p{margin:0;font-size:.85em}body>footer{margin-top:4.5em;padding-bottom:1.5em;text-align:center;font-size:.8rem;color:#aaa}details,nav,p{margin:1em 0}nav ul{list-style:none;margin:0;padding:0}nav li{display:inline-block;margin-right:1em;margin-bottom:.25em}nav a:visited{color:#0064c1}article header h1 a:visited:hover,article header h2 a:visited:hover,nav a:hover{color:#3000c1}ol,ul{margin-top:0;padding-top:0;padding-left:2.5em}p{-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}aside:first-child,p:first-child{margin-top:0}aside:last-child,p:last-child{margin-bottom:0}p+ol,p+ul{margin-top:-.75em}p img,p picture{float:right;margin-bottom:.5em;margin-left:.5em}p picture img{float:none;margin:0}blockquote,dd{padding-left:2.5em}dd{margin-bottom:1em;margin-left:0}dt{font-weight:700}blockquote{margin:0}aside{margin:.5em 0;font-style:italic;color:#aaa}@media (min-width:65rem){aside{position:absolute;right:-12.5rem;width:9.375rem;max-width:9.375rem;margin:0;padding-left:.5em;font-size:.8em;border-left:1px solid #f2f2f2}}section+section{margin-top:2em}h1,h2,h3,h4,h5,h6{margin:1.25em 0 0;line-height:1.2}h1:focus>a[href^="#"][id],h1:hover>a[href^="#"][id],h2:focus>a[href^="#"][id],h2:hover>a[href^="#"][id],h3:focus>a[href^="#"][id],h3:hover>a[href^="#"][id],h4:focus>a[href^="#"][id],h4:hover>a[href^="#"][id],h5:focus>a[href^="#"][id],h5:hover>a[href^="#"][id],h6:focus>a[href^="#"][id],h6:hover>a[href^="#"][id]{opacity:1;transition:opacity 0s,color 300ms ease-out}figure+p,h1+details,h1+p,h2+details,h2+p,h3+details,h3+p,h4+details,h4+p,h5+details,h5+p,h6+details,h6+p{margin-top:.5em}h1>a[href^="#"][id],h2>a[href^="#"][id],h3>a[href^="#"][id],h4>a[href^="#"][id],h5>a[href^="#"][id],h6>a[href^="#"][id]{position:absolute;left:-.5em;opacity:0;background:0 0;color:rgba(0,0,0,.5);transition:opacity 300ms ease-out}h1>a[href^="#"][id]:focus,h1>a[href^="#"][id]:hover,h1>a[href^="#"][id]:target,h2>a[href^="#"][id]:focus,h2>a[href^="#"][id]:hover,h2>a[href^="#"][id]:target,h3>a[href^="#"][id]:focus,h3>a[href^="#"][id]:hover,h3>a[href^="#"][id]:target,h4>a[href^="#"][id]:focus,h4>a[href^="#"][id]:hover,h4>a[href^="#"][id]:target,h5>a[href^="#"][id]:focus,h5>a[href^="#"][id]:hover,h5>a[href^="#"][id]:target,h6>a[href^="#"][id]:focus,h6>a[href^="#"][id]:hover,h6>a[href^="#"][id]:target{opacity:1;box-shadow:none;color:#000;transition:opacity 0s,color 0s}h1>a[href^="#"][id]:target:focus,h2>a[href^="#"][id]:target:focus,h3>a[href^="#"][id]:target:focus,h4>a[href^="#"][id]:target:focus,h5>a[href^="#"][id]:target:focus,h6>a[href^="#"][id]:target:focus{outline:0}h1>a[href^="#"][id]::before,h2>a[href^="#"][id]::before,h3>a[href^="#"][id]::before,h4>a[href^="#"][id]::before,h5>a[href^="#"][id]::before,h6>a[href^="#"][id]::before{content:"•"}h1{font-size:2em}h2{font-size:1.75em}h3{font-size:1.25em}h4{font-size:1.15em}h5,h6{font-size:1em}h6{margin-top:1em;color:#aaa}article+article{margin-top:5em}article header p{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:.6em;color:#aaa}article header p+h1,article header p+h2{margin-top:-.25em}article header h1+p,article header h2+p{margin-top:.25em}article header h1 a,article header h2 a{color:#000;background:url('data:image/svg+xml;charset=utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="1" height="1"%3E%3Crect x="0" y="0.5" width="1" height="0.5" fill="rgba(0, 0, 0, 0.35)"/%3E%3C/svg%3E') left bottom repeat-x}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){article header h1 a,article header h2 a{background:url('data:image/svg+xml;charset=utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="1" height="2"%3E%3Crect x="0" y="1" width="1" height="1" fill="rgba(0, 0, 0, 0.35)"/%3E%3C/svg%3E') left bottom repeat-x}}article header h1 a:visited,article header h2 a:visited{color:#aaa;background:url('data:image/svg+xml;charset=utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="1" height="1"%3E%3Crect x="0" y="0.5" width="1" height="0.5" fill="rgba(170, 170, 170, 0.35)"/%3E%3C/svg%3E') left bottom repeat-x}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){article header h1 a:visited,article header h2 a:visited{background:url('data:image/svg+xml;charset=utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="1" height="2"%3E%3Crect x="0" y="1" width="1" height="1" fill="rgba(170, 170, 170, 0.35)"/%3E%3C/svg%3E') left bottom repeat-x}}article>footer{margin-top:1.5em;font-size:.85em}a,abbr{text-decoration:none}a{color:#0064c1;transition:color 300ms ease-out,box-shadow 300ms ease-out;background:url('data:image/svg+xml;charset=utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="1" height="1"%3E%3Crect x="0" y="0.5" width="1" height="0.5" fill="rgba(0, 100, 193, 0.35)"/%3E%3C/svg%3E') left bottom repeat-x}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){a{background:url('data:image/svg+xml;charset=utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="1" height="2"%3E%3Crect x="0" y="1" width="1" height="1" fill="rgba(0, 100, 193, 0.35)"/%3E%3C/svg%3E') left bottom repeat-x}}a:visited{color:#80b2e0}a:active,a:hover{outline-width:0}a:hover{box-shadow:inset 0 -1px #3000c1;color:#3000c1;transition:color 0s,box-shadow 0s}abbr{margin-right:-.075em;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none;letter-spacing:.075em;font-size:.9em;border-bottom:1px dotted}img,picture{display:block;max-width:100%;margin:0 auto}audio,video{width:100%;max-width:100%}video{-o-object-position:0 -1px;object-position:0 -1px}figure{margin:1em 0 .5em;padding:0}figure figcaption{opacity:.65;font-size:.85em}table{display:inline-block;border-spacing:0;border-collapse:collapse;overflow-x:auto;max-width:100%;text-align:left;vertical-align:top;background:linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 0 0,linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 100% 0;background-attachment:scroll,scroll;background-size:1px 100%,1px 100%;background-repeat:no-repeat,no-repeat}table caption{font-size:.9em;background:#fff}table td,table th{padding:.35em .75em;vertical-align:top;font-size:.9em;border:1px solid #f2f2f2;border-top:0;border-left:0}table td:first-child,table th:first-child{padding-left:0;background-image:linear-gradient(to right,#fff 50%,rgba(255,255,255,0) 100%);background-size:2px 100%;background-repeat:no-repeat}table td:last-child,table th:last-child{padding-right:0;border-right:0;background-image:linear-gradient(to left,#fff 50%,rgba(255,255,255,0) 100%);background-position:100% 0;background-size:2px 100%;background-repeat:no-repeat}table td:only-child,table th:only-child{background-image:linear-gradient(to right,#fff 50%,rgba(255,255,255,0) 100%),linear-gradient(to left,#fff 50%,rgba(255,255,255,0) 100%);background-position:0 0,100% 0;background-size:2px 100%,2px 100%;background-repeat:no-repeat,no-repeat}table th{line-height:1.2}code,kbd,samp,var{font-family:Consolas,"Lucida Console",Monaco,monospace;font-style:normal}pre{overflow-x:auto;font-size:.8em;background:linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 0 0,linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 100% 0;background-attachment:scroll,scroll;background-size:1px 100%,1px 100%;background-repeat:no-repeat,no-repeat}pre>code,summary{display:inline-block}pre>code{overflow-x:visible;box-sizing:border-box;min-width:100%;border-right:3px solid #fff;border-left:1px solid #fff}hr{height:1px;margin:2em 0;border:0;background:#f2f2f2}details[open]{padding-bottom:.5em;border-bottom:1px solid #f2f2f2}summary{font-weight:700;border-bottom:1px dashed;cursor:pointer}summary::-webkit-details-marker{display:none}noscript{color:#d00000}::selection{background:rgba(0,100,193,.15)} diff --git a/default.nix b/default.nix index c6161f83..66a7daff 100644 --- a/default.nix +++ b/default.nix @@ -38,7 +38,7 @@ let (inDirectory ./src) (inDirectory ./app) (inDirectory ./github-action) - ./awsm-css/dist/awsm.min.css + ./css/awsm.min.css ./LICENSE ]; }; diff --git a/src/Data/OpenApi/Compare/PathsPrefixTree.hs b/src/Data/OpenApi/Compare/PathsPrefixTree.hs index b9487f3c..e8b54d2e 100644 --- a/src/Data/OpenApi/Compare/PathsPrefixTree.hs +++ b/src/Data/OpenApi/Compare/PathsPrefixTree.hs @@ -23,7 +23,8 @@ where import Control.Monad import Data.Aeson import Data.Foldable hiding (null, toList) -import qualified Data.HashMap.Strict as HM +import qualified Data.Aeson.Key as Key +import qualified Data.Aeson.KeyMap as KeyMap import Data.Kind import qualified Data.Map as M import Data.Monoid @@ -256,7 +257,7 @@ newtype MergableObject = MergableObject {getMergableObject :: Object} instance Semigroup MergableObject where (MergableObject x) <> (MergableObject y) = - MergableObject $ HM.unionWith mergeValue x y + MergableObject $ KeyMap.unionWith mergeValue x y where mergeValue :: Value -> Value -> Value mergeValue (Object a) (Object b) = @@ -271,6 +272,6 @@ instance Monoid MergableObject where traceObject :: Paths q r a -> Value -> Object traceObject Root (Object o) = o -traceObject Root v = HM.singleton "root" v +traceObject Root v = KeyMap.singleton "root" v traceObject (root `Snoc` s) v = - traceObject root . Object $ HM.singleton (T.pack . show $ s) v + traceObject root . Object $ KeyMap.singleton (Key.fromText . T.pack . show $ s) v diff --git a/src/Data/OpenApi/Compare/Report.hs b/src/Data/OpenApi/Compare/Report.hs index c8191160..0d2c486f 100644 --- a/src/Data/OpenApi/Compare/Report.hs +++ b/src/Data/OpenApi/Compare/Report.hs @@ -198,7 +198,7 @@ showErrs x@(P.PathsPrefixNode currentIssues _) = let -- Extract this pattern if more cases like this arise ( removedPaths :: Maybe (Orientation, [Issue 'APILevel]) , otherIssues :: Set (AnIssue a) - ) = case eqT @a @ 'APILevel of + ) = case eqT @a @'APILevel of Just Refl | (S.toList -> p@((AnIssue ori _) : _), o) <- S.partition diff --git a/src/Data/OpenApi/Compare/Report/Html/Template.hs b/src/Data/OpenApi/Compare/Report/Html/Template.hs index 88da2d6f..a16f1989 100644 --- a/src/Data/OpenApi/Compare/Report/Html/Template.hs +++ b/src/Data/OpenApi/Compare/Report/Html/Template.hs @@ -36,4 +36,4 @@ template = \" awsmCss :: ByteString -awsmCss = $(makeRelativeToProject "awsm-css/dist/awsm.min.css" >>= embedFile) +awsmCss = $(makeRelativeToProject "css/awsm.min.css" >>= embedFile) diff --git a/src/Data/OpenApi/Compare/Validate/OpenApi.hs b/src/Data/OpenApi/Compare/Validate/OpenApi.hs index a50c7b82..8ad53318 100644 --- a/src/Data/OpenApi/Compare/Validate/OpenApi.hs +++ b/src/Data/OpenApi/Compare/Validate/OpenApi.hs @@ -38,7 +38,8 @@ tracedSecuritySchemes :: Traced OpenApi -> Traced (Definitions SecurityScheme) tracedSecuritySchemes oa = traced (ask oa >>> step ComponentsSecurityScheme) - (_componentsSecuritySchemes . _openApiComponents . extract $ oa) + (unSecurityDefinitions . _componentsSecuritySchemes . _openApiComponents . extract $ oa) + where unSecurityDefinitions (SecurityDefinitions ds) = ds tracedResponses :: Traced OpenApi -> Traced (Definitions Response) tracedResponses oa = diff --git a/src/Data/OpenApi/Compare/Validate/Schema.hs b/src/Data/OpenApi/Compare/Validate/Schema.hs index ec4f2e75..f783a1cb 100644 --- a/src/Data/OpenApi/Compare/Validate/Schema.hs +++ b/src/Data/OpenApi/Compare/Validate/Schema.hs @@ -5,7 +5,7 @@ module Data.OpenApi.Compare.Validate.Schema ) where -import Control.Monad.Writer +import Control.Monad (when,unless) import qualified Data.Aeson as A import Data.Coerce import Data.Foldable (for_, toList) diff --git a/src/Data/OpenApi/Compare/Validate/Schema/JsonFormula.hs b/src/Data/OpenApi/Compare/Validate/Schema/JsonFormula.hs index df1cdd80..05e34efb 100644 --- a/src/Data/OpenApi/Compare/Validate/Schema/JsonFormula.hs +++ b/src/Data/OpenApi/Compare/Validate/Schema/JsonFormula.hs @@ -18,10 +18,11 @@ where import Algebra.Lattice import qualified Data.Aeson as A +import qualified Data.Aeson.Key as Key +import qualified Data.Aeson.KeyMap as KeyMap import qualified Data.ByteString.Lazy as BSL import qualified Data.Foldable as F import Data.Functor -import qualified Data.HashMap.Strict as HM import Data.Int import Data.Kind import qualified Data.Map as M @@ -39,7 +40,7 @@ import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Encoding as T import Data.Typeable -import Text.Pandoc.Builder hiding (Format, Null) +import Text.Pandoc.Builder hiding (Format) import Text.Regex.Pcre2 data Bound a = Exclusive !a | Inclusive !a @@ -177,10 +178,10 @@ satisfiesTyped (TArray a) (MaxItems m) = fromIntegral (F.length a) <= m satisfiesTyped (TArray a) (MinItems m) = fromIntegral (F.length a) >= m satisfiesTyped (TArray a) UniqueItems = S.size (S.fromList $ F.toList a) == F.length a -- TODO: could be better #36 satisfiesTyped (TObject o) (Properties props additional _) = - all (`HM.member` o) (M.keys (M.filter propRequired props)) - && all (\(k, v) -> satisfies v $ maybe additional propFormula $ M.lookup k props) (HM.toList o) -satisfiesTyped (TObject o) (MaxProperties m) = fromIntegral (HM.size o) <= m -satisfiesTyped (TObject o) (MinProperties m) = fromIntegral (HM.size o) >= m + all (`KeyMap.member` o) (map Key.fromText $ M.keys (M.filter propRequired props)) + && all (\(k, v) -> satisfies v $ maybe additional propFormula $ M.lookup (Key.toText k) props) (KeyMap.toList o) +satisfiesTyped (TObject o) (MaxProperties m) = fromIntegral (KeyMap.size o) <= m +satisfiesTyped (TObject o) (MinProperties m) = fromIntegral (KeyMap.size o) >= m checkNumberFormat :: Format -> Scientific -> Bool checkNumberFormat "int32" (toRational -> n) = diff --git a/src/Data/OpenApi/Compare/Validate/Schema/Partition.hs b/src/Data/OpenApi/Compare/Validate/Schema/Partition.hs index 379f5422..6547558e 100644 --- a/src/Data/OpenApi/Compare/Validate/Schema/Partition.hs +++ b/src/Data/OpenApi/Compare/Validate/Schema/Partition.hs @@ -16,6 +16,7 @@ where import Algebra.Lattice import Algebra.Lattice.Lifted import Control.Applicative +import Control.Monad (when) import Control.Monad.Reader hiding (ask) import qualified Control.Monad.Reader as R import Control.Monad.State @@ -31,6 +32,7 @@ import qualified Data.List as L import qualified Data.List.NonEmpty as NE import qualified Data.Map as M import Data.Maybe +import Data.Monoid import Data.OpenApi import Data.OpenApi.Compare.Memo import Data.OpenApi.Compare.References @@ -43,7 +45,8 @@ import Data.Ord import qualified Data.Set as S import Data.Text (Text) import qualified Data.Text as T -import Text.Pandoc.Builder hiding (Format, Null) +import Data.Traversable (forM) +import Text.Pandoc.Builder hiding (Format) data PartitionData = DByEnumValue (DNF (S.Set A.Value)) diff --git a/src/Data/OpenApi/Compare/Validate/Schema/Process.hs b/src/Data/OpenApi/Compare/Validate/Schema/Process.hs index 28c898d0..e690cb1e 100644 --- a/src/Data/OpenApi/Compare/Validate/Schema/Process.hs +++ b/src/Data/OpenApi/Compare/Validate/Schema/Process.hs @@ -13,6 +13,7 @@ import Data.Functor.Identity import qualified Data.HashMap.Strict.InsOrd as IOHM import qualified Data.Map as M import Data.Maybe +import Data.Monoid import Data.OpenApi hiding (get) import Data.OpenApi.Compare.Behavior import Data.OpenApi.Compare.Memo @@ -28,6 +29,7 @@ import Data.OpenApi.Compare.Validate.Schema.Traced import Data.OpenApi.Compare.Validate.Schema.TypedJson import Data.Ord import qualified Data.Set as S +import Data.Traversable (forM) -- | A fake writer monad that doesn't actually record anything and allows lazy recursion. newtype Silent w a = Silent {runSilent :: a} diff --git a/stack.yaml b/stack.yaml index a7b84c39..5e4560ca 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,11 +1,19 @@ -resolver: lts-18.27 +resolver: lts-22.17 packages: - . extra-deps: + - base16-bytestring-1.0.2.0 + - freer-simple-1.2.1.2 + - github-0.29 + - http-link-header-1.2.1 - open-union-0.4.0.0 - type-fun-0.1.3 - - github-0.26 - - base16-bytestring-0.1.1.7 - - http-link-header-1.0.3.1 + - typerep-map-0.6.0.0 + +# Required for freer-simple and typerep-map +allow-newer: true + +nix: + packages: [pkg-config zlib]