{-# LANGUAGE UndecidableInstances #-}

module Conduit.Features.Articles.Comments.DeleteComment where

import Conduit.App.Monad (AppM, runService)
import Conduit.DB.Core (MonadDB(..), expectDBNonZero)
import Conduit.Features.Account.Types (UserID(..))
import Conduit.Features.Articles.DB (Comment, assumingUserIsOwner)
import Conduit.Features.Articles.Errors (ArticleError (..))
import Conduit.Features.Articles.Types (CommentID(..))
import Conduit.Identity.Auth (authedUserID, withAuth)
import Database.Esqueleto.Experimental (deleteCount, from, table, valkey, where_, (==.))
import Network.HTTP.Types (status200)
import UnliftIO (MonadUnliftIO)
import Web.Scotty.Trans (ScottyT, captureParam, status)
import Web.Scotty.Trans qualified as Scotty

handleCommentDeletion :: ScottyT AppM ()
handleCommentDeletion :: ScottyT AppM ()
handleCommentDeletion = RoutePattern -> ActionT AppM () -> ScottyT AppM ()
forall (m :: * -> *).
MonadUnliftIO m =>
RoutePattern -> ActionT m () -> ScottyT m ()
Scotty.delete RoutePattern
"/api/articles/:slug/comments/:id" (ActionT AppM () -> ScottyT AppM ())
-> ActionT AppM () -> ScottyT AppM ()
forall a b. (a -> b) -> a -> b
$ (AuthedUser -> ActionT AppM ()) -> ActionT AppM ()
forall (m :: * -> *) c.
(MonadIO m, MonadReader c m, Has JWTInfo c m) =>
(AuthedUser -> ActionT m ()) -> ActionT m ()
withAuth \AuthedUser
user -> do
  CommentID
commentID <- Text -> ActionT AppM Int64
forall a (m :: * -> *).
(Parsable a, Monad m) =>
Text -> ActionT m a
captureParam Text
"id" ActionT AppM Int64
-> (Int64 -> CommentID) -> ActionT AppM CommentID
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> Int64 -> CommentID
CommentID
  AppM (Either ArticleError ()) -> ActionT AppM ()
forall e a. FeatureError e => AppM (Either e a) -> ActionT AppM a
runService (AppM (Either ArticleError ()) -> ActionT AppM ())
-> AppM (Either ArticleError ()) -> ActionT AppM ()
forall a b. (a -> b) -> a -> b
$ CommentID -> UserID -> AppM (Either ArticleError ())
forall (m :: * -> *).
DeleteComment m =>
CommentID -> UserID -> m (Either ArticleError ())
deleteComment CommentID
commentID AuthedUser
user.authedUserID
  Status -> ActionT AppM ()
forall (m :: * -> *). MonadIO m => Status -> ActionT m ()
status Status
status200

deleteComment :: (DeleteComment m) => CommentID -> UserID -> m (Either ArticleError ())
deleteComment :: forall (m :: * -> *).
DeleteComment m =>
CommentID -> UserID -> m (Either ArticleError ())
deleteComment CommentID
commentID UserID
userID = ExceptT ArticleError m () -> m (Either ArticleError ())
forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT do
  m (Either ArticleError ()) -> ExceptT ArticleError m ()
forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
ExceptT (m (Either ArticleError ()) -> ExceptT ArticleError m ())
-> m (Either ArticleError ()) -> ExceptT ArticleError m ()
forall a b. (a -> b) -> a -> b
$ CommentID -> UserID -> m (Either ArticleError ())
forall (m :: * -> *).
DeleteComment m =>
CommentID -> UserID -> m (Either ArticleError ())
deleteCommentByID CommentID
commentID UserID
userID

class (Monad m) => DeleteComment m where
  deleteCommentByID :: CommentID -> UserID -> m (Either ArticleError ())

instance (Monad m, MonadDB m, MonadUnliftIO m) => DeleteComment m where
  deleteCommentByID :: CommentID -> UserID -> m (Either ArticleError ())
  deleteCommentByID :: CommentID -> UserID -> m (Either ArticleError ())
deleteCommentByID CommentID
commentID UserID
userID = ArticleError -> Either DBError Int64 -> Either ArticleError ()
forall e cnt.
(FeatureError e, Num cnt, Ord cnt) =>
e -> Either DBError cnt -> Either e ()
expectDBNonZero ArticleError
ResourceNotFoundEx (Either DBError Int64 -> Either ArticleError ())
-> m (Either DBError Int64) -> m (Either ArticleError ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SqlPersistT m Int64 -> m (Either DBError Int64)
forall a. SqlPersistT m a -> m (Either DBError a)
forall (m :: * -> *) a.
MonadDB m =>
SqlPersistT m a -> m (Either DBError a)
runDB do
    ArticleError
-> UserID
-> CommentID
-> SqlPersistT m Int64
-> SqlPersistT m Int64
forall table id (m :: * -> *) a e.
(OwnableEntity table, SqlKey table id, MonadIO m, Show e) =>
e -> UserID -> id -> SqlPersistT m a -> SqlPersistT m a
assumingUserIsOwner ArticleError
IllegalCommentDelEx UserID
userID CommentID
commentID do
      SqlQuery () -> SqlPersistT m Int64
forall (m :: * -> *) backend.
(MonadIO m, SqlBackendCanWrite backend) =>
SqlQuery () -> ReaderT backend m Int64
deleteCount (SqlQuery () -> SqlPersistT m Int64)
-> SqlQuery () -> SqlPersistT m Int64
forall a b. (a -> b) -> a -> b
$ do
        SqlExpr (Entity Comment)
a <- From (SqlExpr (Entity Comment))
-> SqlQuery (SqlExpr (Entity Comment))
forall a a'. ToFrom a a' => a -> SqlQuery a'
from (forall ent. PersistEntity ent => From (SqlExpr (Entity ent))
table @Comment)
        SqlExpr (Value Bool) -> SqlQuery ()
where_ (SqlExpr (Entity Comment)
a.id SqlExpr (Value (Key Comment))
-> SqlExpr (Value (Key Comment)) -> SqlExpr (Value Bool)
forall typ.
PersistField typ =>
SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool)
==. Int64 -> SqlExpr (Value (Key Comment))
forall entity.
(ToBackendKey SqlBackend entity, PersistField (Key entity)) =>
Int64 -> SqlExpr (Value (Key entity))
valkey CommentID
commentID.unID)