diff options
-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 |