packages feed

poppy-1.0.0: test/Schema/Client/Chapter.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoFieldSelectors #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

{- | Generated Client. Do not edit.

Nested writes live on 'create' / 'update'.
  create-time relation fields are [CreateChild | ConnectChild unique]
  update-time relation fields are Maybe <Rel>Update (replaceWith, create, connect, delete, update, upsert;
  disconnect when the child foreign key is nullable)
-}
module Schema.Client.Chapter
  ( findMany,
    findUnique,
    findUniqueOrFail,
    findFirst,
    findFirstOrFail,
    count,
    create,
    createMany,
    update,
    updateMany,
    upsert,
    delete,
    deleteMany,
    ChapterCreateScalars,
    ChapterUpdateScalars,
    SectionNestedCreate (..),
    SectionNestedUpsert (..),
    SectionsUpdate (..),
    emptySectionsUpdate,
    ChapterQuery (..),
    ChapterUnique (..),
    ChapterUniqueKey (..),
    ChapterUniqueQuery (..),
    emptyQuery,
    uniqueQuery,
    chapterUniqueWhere,
    OmitSelect (..),
    Picked (..),
    ChapterCreate (..),
    ChapterRow (..),
    ChapterSelect (..),
    ChapterPicked (..),
    chapterSelect,
    ChapterUpdate (..),
    ChapterTable,
    chapterId
  )
where

import Data.Maybe (isJust)
import Data.Text (Text)
import Data.UUID (UUID)
import Poppy.Internal.Generated
  ( Db,
    transactionEither,
    fieldColumn,
    toField,
    ORMError (..),
    fromUniqueRows,
    requireFound,
    uniqueOrFail,
    OrderBy,
    applyQueryModifiers,
    matching,
    selectColumns,
    OmitSelect (..),
    Picked (..),
    prepareIncludeRootQuery,
    Where,
    and_,
    eq
  )
import qualified Poppy.Internal.Generated as Delete
  ( deleteMany,
    deleteWhere,
    whereDelete,
    emptyDelete
  )
import qualified Poppy.Internal.Generated as Insert
  ( insert,
    insertMany,
    upsert
  )
import qualified Poppy.Internal.Generated as Ops
  ( findMany,
    findManyWith,
    findFirst,
    findFirstWith,
    count
  )
import qualified Poppy.Internal.Generated as Update
  ( updateWhere,
    updateMany
  )

import Schema.Chapter (ChapterRow (..), ChapterSelect (..), ChapterPicked (..), chapterSelect, chapterSelectColumns, parseChapterPicked, ChapterTable, chapterId)
import qualified Schema.Chapter as ChapterSchema (ChapterCreate (..), ChapterUpdate (..))

import Schema.Include.Chapter (LoadChapter (..), ChapterInclude (..), ChapterRead, toChapterWithPicked)
import Schema.Section (SectionRow (..), SectionTable, sectionId, sectionChapterRef, SectionCreate (..), SectionUpdate (..))
import qualified Schema.Client.Section as Section (SectionUnique (..), sectionUniqueWhere)

data ChapterUnique
  = ById UUID
  deriving (Eq, Show)

data ChapterUniqueKey
  = OnId
  deriving (Eq, Show)

chapterUniqueWhere :: ChapterUnique -> Where ChapterTable
chapterUniqueWhere = \case
  ById v1 -> eq chapterId v1

chapterConflictCols :: ChapterUniqueKey -> [Text]
chapterConflictCols = \case
  OnId -> ["id"]

data SectionNestedCreate
  = CreateSection
      { id :: Maybe UUID, label :: Text
      }
  | ConnectSection Section.SectionUnique
  deriving (Show, Eq)
data SectionNestedUpsert = SectionNestedUpsert
  { where_ :: Section.SectionUnique
  , create :: SectionNestedCreate
  , update :: SectionUpdate
  }
  deriving (Show, Eq)
data SectionsUpdate = SectionsUpdate
  { replaceWith :: Maybe [SectionNestedCreate]
  , create :: [SectionNestedCreate]
  , createMany :: [SectionNestedCreate]
  , connect :: [Section.SectionUnique]
  , delete :: [Section.SectionUnique]
  , update :: [(Section.SectionUnique, SectionUpdate)]
  , upsert :: [SectionNestedUpsert]
  }
  deriving (Show, Eq)

emptySectionsUpdate :: SectionsUpdate
emptySectionsUpdate =
  SectionsUpdate
    { replaceWith = Nothing
    , create = []
    , createMany = []
    , connect = []
    , delete = []
    , update = []
    , upsert = []
    }
data ChapterCreate = ChapterCreate
  { id :: Maybe UUID,
    bookRef :: UUID,
    heading :: Text,
    sections :: [SectionNestedCreate]
  }
  deriving (Show, Eq)

type ChapterCreateScalars = ChapterSchema.ChapterCreate

toChapterCreateScalars :: ChapterCreate -> ChapterCreateScalars
toChapterCreateScalars input =
  ChapterSchema.ChapterCreate
    { id = input.id,
      bookRef = input.bookRef,
      heading = input.heading
    }
data ChapterUpdate = ChapterUpdate
  { bookRef :: Maybe UUID,
    heading :: Maybe Text,
    sections :: Maybe SectionsUpdate
  }
  deriving (Show, Eq)

type ChapterUpdateScalars = ChapterSchema.ChapterUpdate

toChapterUpdateScalars :: ChapterUpdate -> ChapterUpdateScalars
toChapterUpdateScalars input =
  ChapterSchema.ChapterUpdate
    { bookRef = input.bookRef,
      heading = input.heading
    }

create :: ChapterCreate -> Db (Either ORMError ChapterRow)
create input =
  if hasChapterNestedCreate input
    then transactionEither (createWithNested input)
    else Insert.insert @ChapterTable @ChapterRow (toChapterCreateScalars input)

hasChapterNestedCreate :: ChapterCreate -> Bool
hasChapterNestedCreate input =
  not (null input.sections)

createWithNested :: ChapterCreate -> Db (Either ORMError ChapterRow)
createWithNested input = do
  rootResult <- Insert.insert @ChapterTable @ChapterRow (toChapterCreateScalars input)
  case rootResult of
    Left err -> pure (Left err)
    Right row -> do
      nestedResult <-
        sequenceNested
          [ applySectionsCreate row.id input.sections
          ]
      case nestedResult of
        Left err -> pure (Left err)
        Right () -> pure (Right row)

createMany :: [ChapterCreateScalars] -> Db (Either ORMError Int)
createMany = Insert.insertMany @ChapterTable

update :: ChapterUnique -> ChapterUpdate -> Db (Either ORMError ChapterRow)
update key input =
  if hasChapterNestedUpdate input
    then transactionEither (updateWithNested key input)
    else Update.updateWhere @ChapterTable @ChapterRow (chapterUniqueWhere key) (toChapterUpdateScalars input)

hasChapterNestedUpdate :: ChapterUpdate -> Bool
hasChapterNestedUpdate input =
  isJust input.sections

updateWithNested :: ChapterUnique -> ChapterUpdate -> Db (Either ORMError ChapterRow)
updateWithNested key input = do
  updateResult <- Update.updateWhere @ChapterTable @ChapterRow (chapterUniqueWhere key) (toChapterUpdateScalars input)
  case updateResult of
    Left err -> pure (Left err)
    Right row -> do
      nestedResult <-
        sequenceNested
          [ maybe (pure (Right ())) (applySectionsUpdate row.id) input.sections
          ]
      case nestedResult of
        Left err -> pure (Left err)
        Right () -> pure (Right row)

updateMany :: Where ChapterTable -> ChapterUpdateScalars -> Db (Either ORMError Int)
updateMany = Update.updateMany @ChapterTable

upsert :: ChapterUniqueKey -> ChapterCreateScalars -> ChapterUpdateScalars -> Db (Either ORMError ChapterRow)
upsert key createInput updateInput =
  Insert.upsert @ChapterTable @ChapterRow (chapterConflictCols key) createInput updateInput

sequenceNested :: [Db (Either ORMError ())] -> Db (Either ORMError ())
sequenceNested [] = pure (Right ())
sequenceNested (action : rest) = do
  result <- action
  case result of
    Left err -> pure (Left err)
    Right () -> sequenceNested rest
applySectionsCreate :: UUID -> [SectionNestedCreate] -> Db (Either ORMError ())
applySectionsCreate = insertSections
applySectionsUpdate :: UUID -> SectionsUpdate -> Db (Either ORMError ())
applySectionsUpdate parentId ops = do
  replaced <- case ops.replaceWith of
    Nothing -> pure (Right ())
    Just items -> replaceSections parentId items
  case replaced of
    Left err -> pure (Left err)
    Right () ->
      sequenceNested
        [ deleteSections parentId ops.delete
        , updateSectionsRows parentId ops.update
        , upsertSections parentId ops.upsert
        , insertSections parentId ops.create
        , insertSections parentId ops.createMany
        , connectSections parentId ops.connect
        ]
replaceSections :: UUID -> [SectionNestedCreate] -> Db (Either ORMError ())
replaceSections parentId items = do
  result <-
    Delete.deleteWhere $
      Delete.whereDelete (fieldColumn sectionChapterRef <> " = ?") [toField parentId] (Delete.emptyDelete @SectionTable)
  case result of
    Left err -> pure (Left err)
    Right _ -> insertSections parentId items
insertSections :: UUID -> [SectionNestedCreate] -> Db (Either ORMError ())
insertSections parentId = go
  where
    go [] = pure (Right ())
    go (nested : rest) = do
      result <- case nested of
        ConnectSection key -> connectSections parentId [key]
        CreateSection {id, label} -> do
          inserted <- Insert.insert @SectionTable @SectionRow SectionCreate
            { id = id,
              label = label,
              chapterRef = parentId
            }
          pure $ case inserted of
            Left err -> Left err
            Right _ -> Right ()
      case result of
        Left err -> pure (Left err)
        Right () -> go rest
deleteSections :: UUID -> [Section.SectionUnique] -> Db (Either ORMError ())
deleteSections _ [] = pure (Right ())
deleteSections parentId keys = sequenceNested (map deleteOne keys)
  where
    deleteOne key = do
      result <- Delete.deleteMany @SectionTable
        (Section.sectionUniqueWhere key `and_` eq sectionChapterRef parentId)
      pure $ case result of
        Left err -> Left err
        Right _ -> Right ()
updateSectionsRows :: UUID -> [(Section.SectionUnique, SectionUpdate)] -> Db (Either ORMError ())
updateSectionsRows parentId = go
  where
    go [] = pure (Right ())
    go ((key, nested) : rest) = do
      let patched = SectionUpdate { chapterRef = Nothing, label = nested.label }
      result <-
        Update.updateWhere @SectionTable @SectionRow
          (Section.sectionUniqueWhere key `and_` eq sectionChapterRef parentId)
          patched
      case result of
        Left err -> pure (Left err)
        Right _ -> go rest
upsertSections :: UUID -> [SectionNestedUpsert] -> Db (Either ORMError ())
upsertSections parentId = go
  where
    go [] = pure (Right ())
    go (item : rest) = do
      existing <- Ops.findMany @SectionTable @SectionRow (matching (Section.sectionUniqueWhere item.where_))
      result <- case fromUniqueRows existing of
        Left err -> pure (Left err)
        Right Nothing -> insertSections parentId [item.create]
        Right (Just row) ->
          if row.chapterRef == parentId
            then do
              let patched = SectionUpdate { chapterRef = Nothing, label = item.update.label }
              updated <-
                Update.updateWhere @SectionTable @SectionRow
                  (Section.sectionUniqueWhere item.where_ `and_` eq sectionChapterRef parentId)
                  patched
              pure $ case updated of
                Left err -> Left err
                Right _ -> Right ()
            else pure (Left (UniqueViolation "nested upsert would reparent a row owned by another parent"))
      case result of
        Left err -> pure (Left err)
        Right () -> go rest
connectSections :: UUID -> [Section.SectionUnique] -> Db (Either ORMError ())
connectSections _ [] = pure (Right ())
connectSections parentId keys = sequenceNested (map connectOne keys)
  where
    connectOne key = do
      result <-
        Update.updateWhere @SectionTable @SectionRow
          (Section.sectionUniqueWhere key)
          (SectionUpdate
            { chapterRef = Just parentId,
              label = Nothing
            })
      pure $ case result of
        Left err -> Left err
        Right _ -> Right ()

data ChapterQuery include select = ChapterQuery
  { include_ :: include
  , select_ :: select
  , where_ :: Maybe (Where ChapterTable)
  , orderBy_ :: [OrderBy ChapterTable]
  , limit_ :: Maybe Int
  , offset_ :: Maybe Int
  }

data ChapterUniqueQuery include select = ChapterUniqueQuery
  { include_ :: include
  , select_ :: select
  , where_ :: ChapterUnique
  }

emptyQuery :: ChapterQuery () OmitSelect
emptyQuery =
  ChapterQuery {include_ = (), select_ = OmitSelect, where_ = Nothing, orderBy_ = [], limit_ = Nothing, offset_ = Nothing}

uniqueQuery :: ChapterUnique -> ChapterUniqueQuery () OmitSelect
uniqueQuery key =
  ChapterUniqueQuery {include_ = (), select_ = OmitSelect, where_ = key}

class ReadChapter include select where
  findMany :: ChapterQuery include select -> Db [ChapterRead include select]
  findUnique :: ChapterUniqueQuery include select -> Db (Either ORMError (Maybe (ChapterRead include select)))
  findUniqueOrFail :: ChapterUniqueQuery include select -> Db (Either ORMError (ChapterRead include select))
  findFirst :: ChapterQuery include select -> Db (Maybe (ChapterRead include select))
  findFirstOrFail :: ChapterQuery include select -> Db (Either ORMError (ChapterRead include select))

instance (LoadChapter sections) => ReadChapter (ChapterInclude sections) OmitSelect where
  findMany ChapterQuery {include_, where_, orderBy_, limit_, offset_} = do
    roots <- Ops.findMany @ChapterTable @ChapterRow (prepareIncludeRootQuery @ChapterTable (applyQueryModifiers where_ orderBy_ limit_ offset_))
    loadChapter include_ roots
  findUnique ChapterUniqueQuery {where_, include_} = do
    let w = chapterUniqueWhere where_
    roots <- Ops.findMany @ChapterTable @ChapterRow (prepareIncludeRootQuery @ChapterTable (matching w))
    rows <- loadChapter include_ roots
    pure (fromUniqueRows rows)
  findUniqueOrFail q = uniqueOrFail <$> findUnique q
  findFirst ChapterQuery {include_, where_, orderBy_, offset_} = do
    roots <- Ops.findMany @ChapterTable @ChapterRow (prepareIncludeRootQuery @ChapterTable (applyQueryModifiers where_ orderBy_ (Just 1) offset_))
    loaded <- loadChapter include_ roots
    pure $ case loaded of
      [] -> Nothing
      (row : _) -> Just row
  findFirstOrFail q = do
    result <- findFirst q
    pure $ requireFound result (RecordNotFound "No record found matching query")

instance ReadChapter () OmitSelect where
  findMany ChapterQuery {where_, orderBy_, limit_, offset_} =
    Ops.findMany @ChapterTable @ChapterRow (applyQueryModifiers where_ orderBy_ limit_ offset_)
  findUnique ChapterUniqueQuery {where_} = do
    let w = chapterUniqueWhere where_
    rows <- Ops.findMany @ChapterTable @ChapterRow (matching w)
    pure (fromUniqueRows rows)
  findUniqueOrFail q = uniqueOrFail <$> findUnique q
  findFirst ChapterQuery {where_, orderBy_, limit_, offset_} =
    Ops.findFirst @ChapterTable @ChapterRow (applyQueryModifiers where_ orderBy_ limit_ offset_)
  findFirstOrFail q = do
    result <- findFirst q
    pure $ requireFound result (RecordNotFound "No record found matching query")

instance (LoadChapter sections) => ReadChapter (ChapterInclude sections) ChapterSelect where
  findMany ChapterQuery {include_, select_, where_, orderBy_, limit_, offset_} = do
    roots <- Ops.findMany @ChapterTable @ChapterRow (prepareIncludeRootQuery @ChapterTable (applyQueryModifiers where_ orderBy_ limit_ offset_))
    loaded <- loadChapter include_ roots
    pure $ map (toChapterWithPicked select_) loaded
  findUnique ChapterUniqueQuery {where_, include_, select_} = do
    let w = chapterUniqueWhere where_
    roots <- Ops.findMany @ChapterTable @ChapterRow (prepareIncludeRootQuery @ChapterTable (matching w))
    loaded <- loadChapter include_ roots
    let rows = map (toChapterWithPicked select_) loaded
    pure (fromUniqueRows rows)
  findUniqueOrFail q = uniqueOrFail <$> findUnique q
  findFirst ChapterQuery {select_, include_, where_, orderBy_, offset_} = do
    roots <- Ops.findMany @ChapterTable @ChapterRow (prepareIncludeRootQuery @ChapterTable (applyQueryModifiers where_ orderBy_ (Just 1) offset_))
    loaded <- loadChapter include_ roots
    pure $ case loaded of
      [] -> Nothing
      (row : _) -> Just (toChapterWithPicked select_ row)
  findFirstOrFail q = do
    result <- findFirst q
    pure $ requireFound result (RecordNotFound "No record found matching query")

instance ReadChapter () ChapterSelect where
  findMany ChapterQuery {select_, where_, orderBy_, limit_, offset_} =
    Ops.findManyWith
      (parseChapterPicked select_)
      (selectColumns (chapterSelectColumns select_) . applyQueryModifiers where_ orderBy_ limit_ offset_)
  findUnique ChapterUniqueQuery {where_, select_} = do
    let w = chapterUniqueWhere where_
    rows <-
      Ops.findManyWith
        (parseChapterPicked select_)
        (selectColumns (chapterSelectColumns select_) . matching w)
    pure (fromUniqueRows rows)
  findUniqueOrFail q = uniqueOrFail <$> findUnique q
  findFirst ChapterQuery {select_, where_, orderBy_, limit_, offset_} =
    Ops.findFirstWith
      (parseChapterPicked select_)
      (selectColumns (chapterSelectColumns select_) . applyQueryModifiers where_ orderBy_ limit_ offset_)
  findFirstOrFail q = do
    result <- findFirst q
    pure $ requireFound result (RecordNotFound "No record found matching query")

count :: ChapterQuery include select -> Db Int
count ChapterQuery {where_, orderBy_, limit_, offset_} =
  Ops.count @ChapterTable (applyQueryModifiers where_ orderBy_ limit_ offset_)

delete :: ChapterUnique -> Db (Either ORMError Int)
delete key =
  Delete.deleteMany @ChapterTable (chapterUniqueWhere key)

deleteMany :: Where ChapterTable -> Db (Either ORMError Int)
deleteMany = Delete.deleteMany @ChapterTable