diff options
Diffstat (limited to 'overlays')
| -rw-r--r-- | overlays/spm/lib/Spm/Api.hs | 55 | ||||
| -rw-r--r-- | overlays/spm/server/Spm/Server.hs | 40 |
2 files changed, 79 insertions, 16 deletions
diff --git a/overlays/spm/lib/Spm/Api.hs b/overlays/spm/lib/Spm/Api.hs index af7ae94c..ce4ee2d9 100644 --- a/overlays/spm/lib/Spm/Api.hs +++ b/overlays/spm/lib/Spm/Api.hs | |||
| @@ -5,7 +5,8 @@ module Spm.Api | |||
| 5 | , SpmMailbox, SpmDomain | 5 | , SpmMailbox, SpmDomain |
| 6 | , SpmLocal(..), SpmExtension(..) | 6 | , SpmLocal(..), SpmExtension(..) |
| 7 | , SpmMapping(..), SpmMappingState(..), SpmMappingListingItem(..), SpmMappingListing(..) | 7 | , SpmMapping(..), SpmMappingState(..), SpmMappingListingItem(..), SpmMappingListing(..) |
| 8 | , _SpmMappingStateReject | 8 | , _SpmMappingText, _SpmMappingStateReject |
| 9 | , spmMappingAncestors | ||
| 9 | , SpmApi, spmApi | 10 | , SpmApi, spmApi |
| 10 | ) where | 11 | ) where |
| 11 | 12 | ||
| @@ -40,9 +41,6 @@ import Data.Aeson.Casing | |||
| 40 | 41 | ||
| 41 | import Data.Aeson (ToJSON, FromJSON) | 42 | import Data.Aeson (ToJSON, FromJSON) |
| 42 | 43 | ||
| 43 | import Control.Monad | ||
| 44 | import Control.Applicative | ||
| 45 | |||
| 46 | 44 | ||
| 47 | data SpmStyle = SpmWords | SpmConsonants | 45 | data SpmStyle = SpmWords | SpmConsonants |
| 48 | deriving (Eq, Ord, Read, Show, Bounded, Enum) | 46 | deriving (Eq, Ord, Read, Show, Bounded, Enum) |
| @@ -116,20 +114,44 @@ data SpmMapping = SpmMapping | |||
| 116 | { spmMappingLocal :: Maybe SpmLocal | 114 | { spmMappingLocal :: Maybe SpmLocal |
| 117 | , spmMappingExtension :: Maybe SpmExtension | 115 | , spmMappingExtension :: Maybe SpmExtension |
| 118 | } deriving stock (Eq, Ord, Read, Show, Generic, Typeable) | 116 | } deriving stock (Eq, Ord, Read, Show, Generic, Typeable) |
| 117 | |||
| 118 | _SpmMappingText :: Iso' SpmMapping Text | ||
| 119 | _SpmMappingText = iso toText fromText | ||
| 120 | where | ||
| 121 | toText :: SpmMapping -> Text | ||
| 122 | toText SpmMapping{..} = maybe "" (CI.original . unSpmLocal) spmMappingLocal | ||
| 123 | <> maybe "" (("+" <>) . CI.original . unSpmExtension) spmMappingExtension | ||
| 124 | |||
| 125 | fromText :: Text -> SpmMapping | ||
| 126 | fromText t = case ts ^? _Snoc of | ||
| 127 | Nothing -> SpmMapping{ spmMappingLocal = Nothing, spmMappingExtension = Nothing } | ||
| 128 | Just (tInit, tLast) -> SpmMapping | ||
| 129 | { spmMappingLocal = fmap (SpmLocal . CI.mk) . assertNonEmpty $ Text.intercalate extSep tInit | ||
| 130 | , spmMappingExtension = fmap (SpmExtension . CI.mk) $ assertNonEmpty tLast | ||
| 131 | } | ||
| 132 | where | ||
| 133 | extSep = "+" | ||
| 134 | |||
| 135 | ts = Text.splitOn extSep t | ||
| 136 | |||
| 137 | assertNonEmpty :: Text -> Maybe Text | ||
| 138 | assertNonEmpty t' | Text.null t' = Nothing | ||
| 139 | | otherwise = Just t' | ||
| 140 | |||
| 119 | instance FromHttpApiData SpmMapping where | 141 | instance FromHttpApiData SpmMapping where |
| 120 | parseUrlPiece t | 142 | parseUrlPiece = Right . review _SpmMappingText |
| 121 | | [ fmap (SpmLocal . CI.mk) . assertNonEmpty -> spmMappingLocal | ||
| 122 | , fmap (SpmExtension . CI.mk) . assertNonEmpty -> spmMappingExtension | ||
| 123 | ] <- Text.splitOn "+" t | ||
| 124 | , Just () <- void spmMappingLocal <|> void spmMappingExtension | ||
| 125 | = Right SpmMapping{..} | ||
| 126 | | otherwise = Left "Could not parse SpmMapping" | ||
| 127 | where assertNonEmpty :: Text -> Maybe Text | ||
| 128 | assertNonEmpty t' | Text.null t' = Nothing | ||
| 129 | | otherwise = Just t' | ||
| 130 | instance ToHttpApiData SpmMapping where | 143 | instance ToHttpApiData SpmMapping where |
| 131 | toUrlPiece SpmMapping{..} = maybe "" (CI.original . unSpmLocal) spmMappingLocal | 144 | toUrlPiece = view _SpmMappingText |
| 132 | <> maybe "" (("+" <>) . CI.original . unSpmExtension) spmMappingExtension | 145 | |
| 146 | spmMappingAncestors :: SpmMapping -> [SpmMapping] | ||
| 147 | spmMappingAncestors spmMapping = case nextMapping of | ||
| 148 | Nothing -> [] | ||
| 149 | Just next -> next : spmMappingAncestors next | ||
| 150 | where | ||
| 151 | nextMapping = case spmMapping of | ||
| 152 | SpmMapping{ spmMappingLocal, spmMappingExtension = Just _ } -> Just SpmMapping{spmMappingLocal, spmMappingExtension = Nothing} | ||
| 153 | SpmMapping{ spmMappingLocal = Just _ } -> Just SpmMapping{spmMappingLocal = Nothing, spmMappingExtension = Nothing} | ||
| 154 | SpmMapping{} -> Nothing | ||
| 133 | 155 | ||
| 134 | deriveJSON (aesonPrefix trainCase) ''SpmMapping | 156 | deriveJSON (aesonPrefix trainCase) ''SpmMapping |
| 135 | makePrisms ''SpmMappingState | 157 | makePrisms ''SpmMappingState |
| @@ -151,6 +173,7 @@ type SpmApi = "whoami" :> Get '[PlainText, JSON] SpmMailbox | |||
| 151 | :<|> "mappings" :> Get '[JSON] SpmMappingListing | 173 | :<|> "mappings" :> Get '[JSON] SpmMappingListing |
| 152 | :<|> "mappings" :> Capture "mapping" SpmMapping :> Get '[PlainText, JSON] SpmMappingState | 174 | :<|> "mappings" :> Capture "mapping" SpmMapping :> Get '[PlainText, JSON] SpmMappingState |
| 153 | :<|> "mappings" :> Capture "mapping" SpmMapping :> ReqBody '[PlainText, JSON] SpmMappingState :> PatchNoContent | 175 | :<|> "mappings" :> Capture "mapping" SpmMapping :> ReqBody '[PlainText, JSON] SpmMappingState :> PatchNoContent |
| 176 | :<|> "mappings" :> Capture "mapping" SpmMapping :> ReqBody '[PlainText, JSON] SpmMappingState :> PutNoContent | ||
| 154 | 177 | ||
| 155 | spmApi :: Proxy SpmApi | 178 | spmApi :: Proxy SpmApi |
| 156 | spmApi = Proxy | 179 | spmApi = Proxy |
diff --git a/overlays/spm/server/Spm/Server.hs b/overlays/spm/server/Spm/Server.hs index 86d7d02b..d8efd826 100644 --- a/overlays/spm/server/Spm/Server.hs +++ b/overlays/spm/server/Spm/Server.hs | |||
| @@ -222,6 +222,7 @@ spmServer dom mbox = whoami | |||
| 222 | :<|> listMappings | 222 | :<|> listMappings |
| 223 | :<|> getMapping | 223 | :<|> getMapping |
| 224 | :<|> patchMapping | 224 | :<|> patchMapping |
| 225 | :<|> putMapping | ||
| 225 | where | 226 | where |
| 226 | whoami = do | 227 | whoami = do |
| 227 | Entity _ Mailbox{mailboxIdent} <- maybe (throwError err404) return <=< spmSql . getBy $ UniqueMailbox mbox | 228 | Entity _ Mailbox{mailboxIdent} <- maybe (throwError err404) return <=< spmSql . getBy $ UniqueMailbox mbox |
| @@ -313,6 +314,45 @@ spmServer dom mbox = whoami | |||
| 313 | update mmId [ MailboxMappingReject =. view _SpmMappingStateReject mappingState ] | 314 | update mmId [ MailboxMappingReject =. view _SpmMappingStateReject mappingState ] |
| 314 | return NoContent | 315 | return NoContent |
| 315 | 316 | ||
| 317 | putMapping spmMapping mappingState = spmSql $ do | ||
| 318 | Entity mailboxId _ <- maybe (throwError err404) return <=< getBy $ UniqueMailbox mbox | ||
| 319 | |||
| 320 | existing <- selectList | ||
| 321 | [ MailboxMappingLocal ==. (spmMappingLocal spmMapping <&> view (_Wrapped . _Unwrapped)) | ||
| 322 | , MailboxMappingExtension ==. (spmMappingExtension spmMapping <&> view (_Wrapped . _Unwrapped)) | ||
| 323 | , MailboxMappingDomain ==. dom | ||
| 324 | ] | ||
| 325 | [ LimitTo 1 | ||
| 326 | ] | ||
| 327 | unless (null existing) $ | ||
| 328 | throwError err409 | ||
| 329 | |||
| 330 | let go [] = throwError err403 | ||
| 331 | go (SpmMapping{..} : ancestors) = do | ||
| 332 | candidate <- selectList | ||
| 333 | [ MailboxMappingLocal ==. (spmMappingLocal <&> view (_Wrapped . _Unwrapped)) | ||
| 334 | , MailboxMappingExtension ==. (spmMappingExtension <&> view (_Wrapped . _Unwrapped)) | ||
| 335 | , MailboxMappingDomain ==. dom | ||
| 336 | ] | ||
| 337 | [ LimitTo 1 | ||
| 338 | ] | ||
| 339 | case candidate of | ||
| 340 | [Entity _ MailboxMapping{..}] -> | ||
| 341 | unless (mailboxMappingMailbox == mailboxId) $ | ||
| 342 | throwError err403 | ||
| 343 | [] -> go ancestors | ||
| 344 | _other -> throwError err500 | ||
| 345 | in go $ spmMappingAncestors spmMapping | ||
| 346 | |||
| 347 | insert_ MailboxMapping | ||
| 348 | { mailboxMappingLocal = (spmMappingLocal spmMapping <&> view (_Wrapped . _Unwrapped)) | ||
| 349 | , mailboxMappingExtension = (spmMappingExtension spmMapping <&> view (_Wrapped . _Unwrapped)) | ||
| 350 | , mailboxMappingDomain = dom | ||
| 351 | , mailboxMappingMailbox = mailboxId | ||
| 352 | , mailboxMappingReject = view _SpmMappingStateReject mappingState | ||
| 353 | } | ||
| 354 | return NoContent | ||
| 355 | |||
| 316 | main :: IO () | 356 | main :: IO () |
| 317 | main = runSystemdWarp systemdSettings warpSettings =<< mkSpmApp | 357 | main = runSystemdWarp systemdSettings warpSettings =<< mkSpmApp |
| 318 | where | 358 | where |
