scuttlebutt-types 0.1.0 → 0.2.0
raw patch · 10 files changed
+276/−83 lines, 10 filesdep +cryptonitedep +memorydep −bytestring-conversionPVP ok
version bump matches the API change (PVP)
Dependencies added: cryptonite, memory
Dependencies removed: bytestring-conversion
API changes (from Hackage documentation)
- Ssb.Types.Key: instance Data.Aeson.Types.FromJSON.FromJSON Ssb.Types.Key.PublicKey
- Ssb.Types.Key: instance Data.Aeson.Types.ToJSON.ToJSON Ssb.Types.Key.PublicKey
- Ssb.Types.Key: instance GHC.Generics.Generic Ssb.Types.Key.PublicKey
- Ssb.Types.Key: parsePublicKey :: ByteString -> Maybe PublicKey
- Ssb.Types.Link: type BlobLink = String
- Ssb.Types.Link: type GenericLink = String
- Ssb.Types.Link: type MessageLink = String
- Ssb.Types.Message: type HashType = String
+ Ssb.Types.Hash: Hashed :: v -> Hash -> Hashed v
+ Ssb.Types.Hash: Sha256 :: Digest SHA256 -> Hash
+ Ssb.Types.Hash: [hashOf] :: Hashed v -> Hash
+ Ssb.Types.Hash: [hashedValue] :: Hashed v -> v
+ Ssb.Types.Hash: [sha256] :: Hash -> Digest SHA256
+ Ssb.Types.Hash: calcHashed :: ByteString -> (v -> HashType) -> (ByteString -> Maybe v) -> Maybe (Hashed v)
+ Ssb.Types.Hash: data Hashed v
+ Ssb.Types.Hash: formatHash :: Hash -> ByteString
+ Ssb.Types.Hash: instance GHC.Base.Functor Ssb.Types.Hash.Hashed
+ Ssb.Types.Hash: instance GHC.Classes.Eq Ssb.Types.Hash.Hash
+ Ssb.Types.Hash: instance GHC.Classes.Eq v => GHC.Classes.Eq (Ssb.Types.Hash.Hashed v)
+ Ssb.Types.Hash: instance GHC.Classes.Ord Ssb.Types.Hash.Hash
+ Ssb.Types.Hash: instance GHC.Classes.Ord v => GHC.Classes.Ord (Ssb.Types.Hash.Hashed v)
+ Ssb.Types.Hash: instance GHC.Show.Show Ssb.Types.Hash.Hash
+ Ssb.Types.Hash: instance GHC.Show.Show v => GHC.Show.Show (Ssb.Types.Hash.Hashed v)
+ Ssb.Types.Hash: newtype Hash
+ Ssb.Types.Hash: parseSha256 :: ByteString -> Maybe Hash
+ Ssb.Types.Hash: type HashType = Text
+ Ssb.Types.Link: BlobLink :: Hash -> BlobLink
+ Ssb.Types.Link: Link :: Char -> Text -> Text -> Link
+ Ssb.Types.Link: MessageLink :: Hash -> MessageLink
+ Ssb.Types.Link: [linkSigil] :: Link -> Char
+ Ssb.Types.Link: [linkTag] :: Link -> Text
+ Ssb.Types.Link: [linkTo] :: Link -> Text
+ Ssb.Types.Link: [unBlobLink] :: BlobLink -> Hash
+ Ssb.Types.Link: [unMessageLink] :: MessageLink -> Hash
+ Ssb.Types.Link: class IsLink t where toJSONLink = toJSON . toLink fromJSONLink o = do { l <- parseJSON o; either fail pure (fromLink l) }
+ Ssb.Types.Link: data Link
+ Ssb.Types.Link: formatLink :: Link -> Text
+ Ssb.Types.Link: fromJSONLink :: IsLink t => Value -> Parser t
+ Ssb.Types.Link: fromLink :: IsLink t => Link -> Either String t
+ Ssb.Types.Link: instance Data.Aeson.Types.FromJSON.FromJSON Ssb.Types.Link.BlobLink
+ Ssb.Types.Link: instance Data.Aeson.Types.FromJSON.FromJSON Ssb.Types.Link.Link
+ Ssb.Types.Link: instance Data.Aeson.Types.FromJSON.FromJSON Ssb.Types.Link.MessageLink
+ Ssb.Types.Link: instance Data.Aeson.Types.ToJSON.ToJSON Ssb.Types.Link.BlobLink
+ Ssb.Types.Link: instance Data.Aeson.Types.ToJSON.ToJSON Ssb.Types.Link.Link
+ Ssb.Types.Link: instance Data.Aeson.Types.ToJSON.ToJSON Ssb.Types.Link.MessageLink
+ Ssb.Types.Link: instance GHC.Classes.Eq Ssb.Types.Link.BlobLink
+ Ssb.Types.Link: instance GHC.Classes.Eq Ssb.Types.Link.Link
+ Ssb.Types.Link: instance GHC.Classes.Eq Ssb.Types.Link.MessageLink
+ Ssb.Types.Link: instance GHC.Classes.Ord Ssb.Types.Link.BlobLink
+ Ssb.Types.Link: instance GHC.Classes.Ord Ssb.Types.Link.Link
+ Ssb.Types.Link: instance GHC.Classes.Ord Ssb.Types.Link.MessageLink
+ Ssb.Types.Link: instance GHC.Show.Show Ssb.Types.Link.BlobLink
+ Ssb.Types.Link: instance GHC.Show.Show Ssb.Types.Link.Link
+ Ssb.Types.Link: instance GHC.Show.Show Ssb.Types.Link.MessageLink
+ Ssb.Types.Link: instance Ssb.Types.Link.IsLink Ssb.Types.Link.BlobLink
+ Ssb.Types.Link: instance Ssb.Types.Link.IsLink Ssb.Types.Link.FeedLink
+ Ssb.Types.Link: instance Ssb.Types.Link.IsLink Ssb.Types.Link.MessageLink
+ Ssb.Types.Link: newtype BlobLink
+ Ssb.Types.Link: newtype MessageLink
+ Ssb.Types.Link: parseLink :: Text -> Either String Link
+ Ssb.Types.Link: toJSONLink :: IsLink t => t -> Value
+ Ssb.Types.Link: toLink :: IsLink t => t -> Link
+ Ssb.Types.Message: messageLink :: Hashed (Message a) -> MessageLink
+ Ssb.Types.Message: parseMessage :: FromJSON a => ByteString -> Maybe (Hashed (Message a))
- Ssb.Types.Message: About :: GenericLink -> Maybe Text -> Maybe AboutImage -> About
+ Ssb.Types.Message: About :: Link -> Maybe Text -> Maybe AboutImage -> About
- Ssb.Types.Message: Mention :: GenericLink -> Mention
+ Ssb.Types.Message: Mention :: Link -> Mention
- Ssb.Types.Message: Pub :: Text -> Int -> PublicKey -> Pub
+ Ssb.Types.Message: Pub :: Text -> Int -> FeedLink -> Pub
- Ssb.Types.Message: Vote :: GenericLink -> Int -> Maybe Text -> Vote
+ Ssb.Types.Message: Vote :: Link -> Int -> Maybe Text -> Vote
- Ssb.Types.Message: [about] :: About -> GenericLink
+ Ssb.Types.Message: [about] :: About -> Link
- Ssb.Types.Message: [mentionLink] :: Mention -> GenericLink
+ Ssb.Types.Message: [mentionLink] :: Mention -> Link
- Ssb.Types.Message: [pubKey] :: Pub -> PublicKey
+ Ssb.Types.Message: [pubKey] :: Pub -> FeedLink
- Ssb.Types.Message: [voteLink] :: Vote -> GenericLink
+ Ssb.Types.Message: [voteLink] :: Vote -> Link
Files
- CHANGELOG +8/−0
- scuttlebutt-types.cabal +10/−5
- src/Ssb/Types/Hash.hs +47/−0
- src/Ssb/Types/Key.hs +5/−34
- src/Ssb/Types/Link.hs +125/−12
- src/Ssb/Types/Message.hs +17/−9
- test/HashSpec.hs +16/−0
- test/KeySpec.hs +2/−2
- test/LinkSpec.hs +26/−0
- test/MessageSpec.hs +20/−21
CHANGELOG view
@@ -1,3 +1,11 @@+scuttlebutt-types (0.2.0) unstable; urgency=medium++ * Improved Link types.+ * Added Hash type.+ * Some API changes.++ -- Joey Hess <id@joeyh.name> Thu, 05 Oct 2017 17:40:48 -0400+ scuttlebutt-types (0.1.0) unstable; urgency=low * First release, split off from haskell-scuttlebutt.
scuttlebutt-types.cabal view
@@ -1,5 +1,5 @@ Name: scuttlebutt-types-Version: 0.1.0+Version: 0.2.0 License: BSD3 License-File: LICENSE Author: bkil.hu, Peter Ferenc Hajdu, Joey Hess@@ -23,16 +23,18 @@ Exposed-Modules: Ssb.Types Ssb.Types.Message- Ssb.Types.Key Ssb.Types.Link+ Ssb.Types.Key+ Ssb.Types.Hash Build-Depends: base (>= 4.7 && < 5), bytestring,- bytestring-conversion, base64-bytestring, aeson, ed25519,- text+ cryptonite,+ text,+ memory Default-Language: Haskell2010 Test-Suite scuttlebutt-types-test@@ -44,10 +46,13 @@ scuttlebutt-types, hspec, bytestring,- aeson+ aeson,+ text Other-Modules: MessageSpec+ LinkSpec KeySpec+ HashSpec Ghc-Options: -Wall -fno-warn-tabs -threaded -rtsopts -with-rtsopts=-N Default-Language: Haskell2010
+ src/Ssb/Types/Hash.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE PackageImports, OverloadedStrings #-}++module Ssb.Types.Hash where++import Data.ByteString+import qualified Data.ByteString.Lazy as L+import Data.ByteArray (convert)+import qualified Data.ByteString.Base64 as B64+import qualified Data.Text as T+import "cryptonite" Crypto.Hash as Crypto++newtype Hash = Sha256 { sha256 :: Crypto.Digest Crypto.SHA256 }+ deriving (Eq, Ord)++instance Show Hash where+ show = show . formatHash ++-- | Decodes a SHA256 hash, which is base64 encoded.+parseSha256 :: ByteString -> Maybe Hash+parseSha256 b = Sha256 <$> either+ (const Nothing)+ Crypto.digestFromByteString+ (B64.decode b)++-- | Formats a hash as a base64 encoded string.+formatHash :: Hash -> ByteString+formatHash (Sha256 { sha256 = s }) = B64.encode (convert s)++type HashType = T.Text++-- | A value accompanied with the hash of the ByteString that it was+-- deserialized from.+data Hashed v = Hashed+ { hashedValue :: v+ , hashOf :: Hash+ }+ deriving (Show, Eq, Ord)++instance Functor Hashed where+ fmap f h = h { hashedValue = f (hashedValue h) }++calcHashed :: L.ByteString -> (v -> HashType) -> (L.ByteString -> Maybe v) -> Maybe (Hashed v)+calcHashed b gettype f = do+ v <- f b+ case gettype v of+ "sha256" -> Just $ Hashed v $ Sha256 (hashlazy b)+ _ -> Nothing
src/Ssb/Types/Key.hs view
@@ -1,45 +1,17 @@-{-# LANGUAGE DeriveGeneric, OverloadedStrings #-}--module Ssb.Types.Key (- PublicKey(..),- parsePublicKey,- parseEd25519PublicKey,- formatPublicKey,-) where+module Ssb.Types.Key where import Data.ByteString-import Data.Text.Encoding (encodeUtf8, decodeUtf8)+import Data.Text.Encoding (decodeUtf8) import qualified Data.Text as T import qualified Data.ByteString.Base64 as B64-import qualified Data.Aeson as Aeson import qualified Crypto.Sign.Ed25519 as Ed-import GHC.Generics-import Data.Monoid newtype PublicKey = Ed25519PublicKey { ed25519Key :: Ed.PublicKey }- deriving (Eq, Ord, Generic)--instance Aeson.FromJSON PublicKey where- parseJSON = Aeson.withText "PublicKey" $ \t ->- case parsePublicKey (encodeUtf8 t) of- Just pk -> return pk- Nothing -> fail ("public key decode failure: " ++ T.unpack t)--instance Aeson.ToJSON PublicKey where- toJSON = Aeson.String . decodeUtf8 . formatPublicKey+ deriving (Eq, Ord) instance Show PublicKey where show = T.unpack . decodeUtf8 . formatPublicKey --- | Parses a SSB public key, which is base64 encoded, and has a prefix of--- "@" and a suffix specifying the type of the key.-parsePublicKey :: ByteString -> Maybe PublicKey-parsePublicKey b = do- b' <- stripPrefix "@" b- case stripSuffix ".ed25519" b' of- Just b'' -> parseEd25519PublicKey b''- Nothing -> Nothing- -- | Decodes a SSB Ed25519 public key, which is base64 encoded. parseEd25519PublicKey :: ByteString -> Maybe PublicKey parseEd25519PublicKey b = Ed25519PublicKey <$> maybeEd25519PublicKey@@ -50,8 +22,7 @@ (Just . Ed.PublicKey) decodedPublicKey --- | Formats a SSB public key to a base64 encoded string with a prefix of--- "@" and suffix specifying the type of the key.+-- | Formats a SSB public key as a base64 encoded string. formatPublicKey :: PublicKey -> ByteString formatPublicKey (Ed25519PublicKey { ed25519Key = k }) =- "@" <> B64.encode (Ed.unPublicKey k) <> ".ed25519"+ B64.encode (Ed.unPublicKey k)
src/Ssb/Types/Link.hs view
@@ -1,26 +1,139 @@--- | TODO newtypes for everything+{-# LANGUAGE OverloadedStrings #-} -module Ssb.Types.Link where+module Ssb.Types.Link (+ Link(..),+ parseLink,+ formatLink,+ FeedLink(..),+ MessageLink(..),+ BlobLink(..),+ IsLink(..),+) where import Ssb.Types.Key+import Ssb.Types.Hash -import Data.Aeson+import Data.Monoid+import Data.Text.Encoding (encodeUtf8, decodeUtf8)+import qualified Data.Aeson as Aeson+import qualified Data.Aeson.Types as Aeson+import qualified Data.Text as T+import qualified Crypto.Sign.Ed25519 as Ed --- | A link to a message.-type MessageLink = String+-- | A link to a message, feed, or blob.+data Link = Link+ { linkSigil :: Char+ -- ^ sigil that starts the link, eg '@'+ , linkTo :: T.Text+ -- ^ thing being linked to (base64 encoded)+ , linkTag :: T.Text+ -- ^ hash or key algorithm of thing being linked to, eg "sha256"+ } deriving (Eq, Ord) +instance Show Link where+ show = T.unpack . formatLink++instance Aeson.FromJSON Link where+ parseJSON = Aeson.withText "Link" $ either fail pure . parseLink++instance Aeson.ToJSON Link where+ toJSON = Aeson.String . formatLink++-- | Parses a Link eg+-- "@LA9HYf5rnUJFHHTklKXLLRyrEytayjbFZRo76Aj/qKs=.ed25519"+parseLink :: T.Text -> Either String Link+parseLink t+ | T.null t = Left "empty link"+ | otherwise =+ let (sigil, rest) = T.splitAt 1 t+ (linkto, linktag) = T.breakOnEnd "." rest+ in if T.null linktag+ then Left "missing linkTag"+ else if T.null linkto+ then Left "empty link"+ else Right $ Link+ { linkSigil = T.head sigil+ , linkTo = T.init linkto+ , linkTag = linktag+ }++-- | Formats a Link.+formatLink :: Link -> T.Text+formatLink l = T.singleton (linkSigil l) <> linkTo l <> "." <> linkTag l++-- | Class of types that are links.+class IsLink t where+ fromLink :: Link -> Either String t++ toLink :: t -> Link++ toJSONLink :: t -> Aeson.Value+ toJSONLink = Aeson.toJSON . toLink++ fromJSONLink :: Aeson.Value -> Aeson.Parser t+ fromJSONLink o = do+ l <- Aeson.parseJSON o+ either fail pure (fromLink l)+ -- | A link to a feed. newtype FeedLink = FeedLink { unFeedLink :: PublicKey } deriving (Show, Eq, Ord) -instance FromJSON FeedLink where- parseJSON d = FeedLink <$> parseJSON d+instance IsLink FeedLink where+ fromLink l+ | linkSigil l == '@' && linkTag l == "ed25519" =+ maybe (Left "ed25519 key parse failed")+ (Right . FeedLink)+ (parseEd25519PublicKey $ encodeUtf8 $ linkTo l)+ | otherwise = Left "wrong sigil for feed link"+ toLink fl = Link '@' linkto "ed25519"+ where+ linkto = decodeUtf8 $ Ed.unPublicKey $ ed25519Key $ unFeedLink fl -instance ToJSON FeedLink where- toJSON = toJSON . unFeedLink+instance Aeson.ToJSON FeedLink where+ toJSON = toJSONLink +instance Aeson.FromJSON FeedLink where+ parseJSON = fromJSONLink++-- | A link to a message.+newtype MessageLink = MessageLink { unMessageLink :: Hash }+ deriving (Show, Eq, Ord)++instance IsLink MessageLink where+ fromLink l+ | linkSigil l == '%' && linkTag l == "sha256" =+ maybe (Left "sha256 hash parse failed")+ (Right . MessageLink)+ (parseSha256 $ encodeUtf8 $ linkTo l)+ | otherwise = Left "wrong sigil for message link"+ toLink fl = Link '%' linkto "sha256"+ where+ linkto = decodeUtf8 $ formatHash $ unMessageLink fl++instance Aeson.ToJSON MessageLink where+ toJSON = toJSONLink++instance Aeson.FromJSON MessageLink where+ parseJSON = fromJSONLink+ -- | A link to a blob.-type BlobLink = String+newtype BlobLink = BlobLink { unBlobLink :: Hash }+ deriving (Show, Eq, Ord) --- | A link to a message, feed, or blob.-type GenericLink = String+instance IsLink BlobLink where+ fromLink l+ | linkSigil l == '&' && linkTag l == "sha256" =+ maybe (Left "sha256 hash parse failed")+ (Right . BlobLink)+ (parseSha256 $ encodeUtf8 $ linkTo l)+ | otherwise = Left "wrong sigil for blob link"+ toLink fl = Link '&' linkto "sha256"+ where+ linkto = decodeUtf8 $ formatHash $ unBlobLink fl++instance Aeson.ToJSON BlobLink where+ toJSON = toJSONLink++instance Aeson.FromJSON BlobLink where+ parseJSON = fromJSONLink
src/Ssb/Types/Message.hs view
@@ -2,9 +2,10 @@ module Ssb.Types.Message ( Signature,- HashType, Message(..),+ messageLink, AnyContent(..),+ parseMessage, narrowParse, PrivateContent(..), Post(..),@@ -17,25 +18,24 @@ parseMessageType ) where -import Ssb.Types.Key import Ssb.Types.Link+import Ssb.Types.Hash import GHC.Generics import Data.Aeson import Data.Aeson.Types import Data.Int (Int64)-import qualified Data.Text as T import Data.Maybe import Data.List import Data.Char import Control.Applicative+import qualified Data.Text as T+import qualified Data.ByteString.Lazy as L import Prelude hiding (sequence) type Signature = String -type HashType = String- data Message a = Message { previous :: Maybe MessageLink , author :: FeedLink@@ -52,6 +52,14 @@ instance Functor Message where fmap f m = m { content = f (content m) } +messageLink :: Hashed (Message a) -> MessageLink+messageLink hm = MessageLink (hashOf hm)++-- | Parses a JSON formatted message, and accompanies it with the +-- hash that was originally used for the message.+parseMessage :: FromJSON a => L.ByteString -> Maybe (Hashed (Message a))+parseMessage b = calcHashed b hash decode+ -- | Parsing a Message AnyContent allows parsing the message envelope, -- regardless of the type of content in the message. data AnyContent = AnyContent { fromAnyContent :: Value }@@ -103,7 +111,7 @@ -- | A generic reference to other feeds, entities, or blobs data Mention = Mention- { mentionLink :: GenericLink+ { mentionLink :: Link } deriving (Show, Eq, Generic) instance FromJSON Mention where@@ -113,7 +121,7 @@ -- They can be used to set a name or picture for users, files, or messages. -- However, they're most commonly published about users. data About = About- { about :: GenericLink+ { about :: Link , name :: Maybe T.Text , image :: Maybe AboutImage } deriving (Show, Eq, Generic)@@ -149,7 +157,7 @@ -- | Vote-messages signal approval about someone or something. -- Votes can be on users, messages, or blobs. data Vote = Vote- { voteLink :: GenericLink+ { voteLink :: Link , voteValue :: Int , voteExpression :: Maybe T.Text } deriving (Show, Eq, Generic)@@ -179,7 +187,7 @@ data Pub = Pub { pubHost :: T.Text , pubPort :: Int- , pubKey :: PublicKey+ , pubKey :: FeedLink } deriving (Show, Eq, Generic) instance FromJSON Pub where
+ test/HashSpec.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE OverloadedStrings #-}++module HashSpec where++import Ssb.Types.Hash+import Test.Hspec+import Data.ByteString++base64sha256 :: ByteString+base64sha256 = "MPB9vxHO0pvi2ve2wh6Do05ZrV7P6ZjUQ+IEYnzLfTs="++spec :: Spec+spec = describe "sha256 hash" $ do+ it "can be deserialized and serialized from base64 bytestrings" $ do+ let (Just hash) = parseSha256 base64sha256+ (formatHash hash) `shouldBe` base64sha256
test/KeySpec.hs view
@@ -7,10 +7,10 @@ import Data.ByteString base64PublicKey :: ByteString-base64PublicKey = "@opBinLmYiID9SMEoZJPH60LmduSYAR7J00P7/Gj9vHw=.ed25519"+base64PublicKey = "opBinLmYiID9SMEoZJPH60LmduSYAR7J00P7/Gj9vHw=" spec :: Spec spec = describe "public key" $ do it "can be deserialized and serialized from base64 bytestrings" $ do- let (Just publicKey) = parsePublicKey base64PublicKey+ let (Just publicKey) = parseEd25519PublicKey base64PublicKey (formatPublicKey publicKey) `shouldBe` base64PublicKey
+ test/LinkSpec.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE OverloadedStrings #-}++module LinkSpec where++import Ssb.Types.Link+import Ssb.Types.Key+import Test.Hspec+import Data.Text+import Data.Maybe++feedLink :: Text+feedLink = "@LA9HYf5rnUJFHHTklKXLLRyrEytayjbFZRo76Aj/qKs=.ed25519"++spec :: Spec+spec = do+ describe "Link" $ do+ it "parses" $ do+ let (Right l) = parseLink feedLink+ linkSigil l `shouldBe` '@'+ linkTo l `shouldBe` "LA9HYf5rnUJFHHTklKXLLRyrEytayjbFZRo76Aj/qKs="+ linkTag l `shouldBe` "ed25519"+ formatLink l `shouldBe` feedLink+ describe "FeedLink" $ do+ it "parses" $ do+ let (Right l) = fromLink =<< parseLink feedLink+ unFeedLink l `shouldBe` fromJust (parseEd25519PublicKey "LA9HYf5rnUJFHHTklKXLLRyrEytayjbFZRo76Aj/qKs=")
test/MessageSpec.hs view
@@ -5,10 +5,11 @@ import Test.Hspec import Ssb.Types.Message-import Ssb.Types.Key import Ssb.Types.Link+import Ssb.Types.Key+import Ssb.Types.Hash import Data.Aeson-import Data.ByteString.Lazy+import Data.ByteString.Lazy (ByteString) import Data.Maybe import Prelude hiding (sequence)@@ -50,7 +51,7 @@ messageSpec = describe "message" $ do it "extracts message content" $ do let (Just message) = (decode originalMessage) :: Maybe (Message AnyContent)- previous message `shouldBe` Just "%3AWRZYdDHKOmLOWvzHbwJFjx9g8hOQH/NXZkwciA63Y=.sha256"+ previous message `shouldBe` MessageLink <$> parseSha256 "3AWRZYdDHKOmLOWvzHbwJFjx9g8hOQH/NXZkwciA63Y=" author message `shouldBe` fromJust (FeedLink <$> parseEd25519PublicKey "EMovhfIrFk4NihAKnRNhrfRaqIhBv1Wj8pTxJNgvCCY=") sequence message `shouldBe` 184 timestamp message `shouldBe` 1449954503740@@ -61,16 +62,16 @@ postSpec = describe "post" $ do it "extracts message post" $ do let (Just message) = (decode originalMessage) :: Maybe (Message Post)- content message `shouldBe` Post- { text = "@johnny use https://github.com/ssbc/ssb-msgs and https://github.com/ssbc/ssb-ref is probably useful too."- , channel = Nothing- , root = Just "%rf1JvoFg1pHE6TkuuMxsjBNevFck7LQvXGhkLMNlaYs=.sha256"- , branch = Just "%G37a4PUTbysiETQazyYATwLkfn/ZzigmIheAx905MLU=.sha256"- , recps = Nothing- , mentions = Just- [ Mention { mentionLink = "@dnr1swLSAgf36g+FzGjNLgmytj2IIyDaYeKZ7F5GdzY=.ed25519" }- ]- }+ text (content message) `shouldBe`+ "@johnny use https://github.com/ssbc/ssb-msgs and https://github.com/ssbc/ssb-ref is probably useful too."+ channel (content message) `shouldBe` Nothing+ root (content message) `shouldBe`+ MessageLink <$> parseSha256 "rf1JvoFg1pHE6TkuuMxsjBNevFck7LQvXGhkLMNlaYs="+ branch (content message) `shouldBe`+ MessageLink <$> parseSha256 "G37a4PUTbysiETQazyYATwLkfn/ZzigmIheAx905MLU="+ recps (content message) `shouldBe` Nothing+ map (linkTo . mentionLink) <$> (mentions (content message)) `shouldBe`+ Just [ "dnr1swLSAgf36g+FzGjNLgmytj2IIyDaYeKZ7F5GdzY=" ] it "does not parse content that is not a post" $ do (decode originalAbout :: Maybe (Message Post)) `shouldBe` Nothing@@ -101,10 +102,10 @@ aboutSpec = describe "about" $ do it "extracts message about" $ do let Just message = (decode originalAbout) :: Maybe (Message About)- about (content message) `shouldBe` "@4RUNgFpvI4ZHH6mPPC9GLzsdv33Cs2JGMMcs8rJSMuU=.ed25519"+ linkTo (about (content message)) `shouldBe` "4RUNgFpvI4ZHH6mPPC9GLzsdv33Cs2JGMMcs8rJSMuU=" name (content message) `shouldBe` Nothing fmap aboutImageLink (image (content message)) `shouldBe`- Just "&Ro1xCJNDIwwKul2SLSU1e4hrwekPcBI6OrBFN0JWnQM=.sha256"+ BlobLink <$> parseSha256 "Ro1xCJNDIwwKul2SLSU1e4hrwekPcBI6OrBFN0JWnQM=" fmap aboutImageSize (image (content message)) `shouldBe` Just (Just 502712) fmap aboutImageType (image (content message)) `shouldBe`@@ -162,11 +163,9 @@ voteSpec = describe "vote" $ do it "extracts message vote" $ do let Just message = (decode originalVote) :: Maybe (Message Vote)- content message `shouldBe` Vote- { voteLink = "%lu9JsdUl4astKykyaVFdkLb7//tS0K08BMs6dT+fErA=.sha256"- , voteValue = 1- , voteExpression = Nothing- }+ voteValue (content message) `shouldBe` 1+ voteExpression (content message) `shouldBe` Nothing+ linkTo (voteLink (content message)) `shouldBe` "lu9JsdUl4astKykyaVFdkLb7//tS0K08BMs6dT+fErA=" -- Not a real private message content, because the test couldn't decrypt it anyway. originalPrivateContent :: ByteString@@ -213,5 +212,5 @@ let Just message = (decode originalPub) :: Maybe (Message Pub) pubHost (content message) `shouldBe` "ssb.hypersignal.xyz" pubPort (content message) `shouldBe` 8008- Just (pubKey (content message)) `shouldBe`+ Just (unFeedLink (pubKey (content message))) `shouldBe` parseEd25519PublicKey "XRg7pXoQqsWDDk4dmgvSWHUqzwS6BmqMo4IdbMKPjWA="