module IHP.IDE.SchemaDesigner.Controller.Tables where

import IHP.ControllerPrelude
import IHP.IDE.ToolServer.Types

import IHP.IDE.SchemaDesigner.View.Tables.New
import IHP.IDE.SchemaDesigner.View.Tables.Show
import IHP.IDE.SchemaDesigner.View.Tables.Index
import IHP.IDE.SchemaDesigner.View.Tables.Edit

import IHP.IDE.SchemaDesigner.Types
import IHP.IDE.SchemaDesigner.View.Layout (findStatementByName, replace, schemaDesignerLayout)
import qualified IHP.SchemaCompiler as SchemaCompiler
import IHP.IDE.SchemaDesigner.Controller.Helper
import IHP.IDE.SchemaDesigner.Controller.Validation

instance Controller TablesController where
    beforeAction :: IO ()
beforeAction = (?context::ControllerContext) =>
((?context::ControllerContext) => Layout) -> IO ()
((?context::ControllerContext) => Layout) -> IO ()
setLayout (?context::ControllerContext) => Layout
Html -> Html
schemaDesignerLayout

    action :: TablesController -> IO ()
action TablesController
TablesAction = do
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        IndexView -> IO ()
forall view controller.
(View view, ?theAction::controller, ?context::ControllerContext,
 ?modelContext::ModelContext) =>
view -> IO ()
render IndexView :: [Statement] -> IndexView
IndexView { [Statement]
$sel:statements:IndexView :: [Statement]
statements :: [Statement]
.. }

    action ShowTableAction { Text
$sel:tableName:TablesAction :: TablesController -> Text
tableName :: Text
tableName } = do
        let name :: Text
name = Text
tableName
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        let (Just Statement
table) = Text -> [Statement] -> Maybe Statement
forall (t :: * -> *).
Foldable t =>
Text -> t Statement -> Maybe Statement
findStatementByName Text
name [Statement]
statements
        let generatedHaskellCode :: Text
generatedHaskellCode = [Statement] -> Statement -> Text
SchemaCompiler.compileStatementPreview [Statement]
statements Statement
table
        ShowView -> IO ()
forall view controller.
(View view, ?theAction::controller, ?context::ControllerContext,
 ?modelContext::ModelContext) =>
view -> IO ()
render ShowView :: [Statement] -> Text -> Statement -> ShowView
ShowView { [Statement]
Text
Statement
$sel:table:ShowView :: Statement
$sel:name:ShowView :: Text
$sel:statements:ShowView :: [Statement]
table :: Statement
statements :: [Statement]
name :: Text
.. }

    action TablesController
NewTableAction = do
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        NewTableView -> IO ()
forall view controller.
(View view, ?theAction::controller, ?context::ControllerContext,
 ?modelContext::ModelContext) =>
view -> IO ()
render NewTableView :: [Statement] -> NewTableView
NewTableView { [Statement]
$sel:statements:NewTableView :: [Statement]
statements :: [Statement]
.. }

    action TablesController
CreateTableAction = do
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        let tableName :: Text
tableName = ByteString -> Text
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"tableName"
        let validationResult :: ValidatorResult
validationResult = Text
tableName Text -> (Text -> ValidatorResult) -> ValidatorResult
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> [Statement] -> Maybe Text -> Text -> ValidatorResult
validateTable [Statement]
statements Maybe Text
forall a. Maybe a
Nothing
        case ValidatorResult
validationResult of
            Failure Text
message -> do
                (?context::ControllerContext) => Text -> IO ()
Text -> IO ()
setErrorMessage Text
message
                TablesController -> IO ()
forall action.
(?context::ControllerContext, HasPath action) =>
action -> IO ()
redirectTo TablesController
TablesAction
            ValidatorResult
Success -> do
                ([Statement] -> [Statement]) -> IO ()
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
([Statement] -> [Statement]) -> IO ()
updateSchema (Text -> [Statement] -> [Statement]
addTable Text
tableName)
                TablesController -> IO ()
forall action.
(?context::ControllerContext, HasPath action) =>
action -> IO ()
redirectTo ShowTableAction :: Text -> TablesController
ShowTableAction { Text
tableName :: Text
$sel:tableName:TablesAction :: Text
.. }

    action EditTableAction { Int
Text
$sel:tableId:TablesAction :: TablesController -> Int
tableId :: Int
tableName :: Text
$sel:tableName:TablesAction :: TablesController -> Text
.. } = do
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        let tableId :: Int
tableId = ByteString -> Int
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"tableId"
        EditTableView -> IO ()
forall view controller.
(View view, ?theAction::controller, ?context::ControllerContext,
 ?modelContext::ModelContext) =>
view -> IO ()
render EditTableView :: [Statement] -> Text -> Int -> EditTableView
EditTableView { Int
[Statement]
Text
$sel:tableId:EditTableView :: Int
$sel:tableName:EditTableView :: Text
$sel:statements:EditTableView :: [Statement]
tableId :: Int
statements :: [Statement]
tableName :: Text
.. }

    action TablesController
UpdateTableAction = do
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        let tableName :: Text
tableName = ByteString -> Text
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"tableName"
        let tableId :: Int
tableId = ByteString -> Int
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"tableId"
        let oldTableName :: Text
oldTableName = (Proxy "name" -> CreateTable -> Text
forall model (name :: Symbol) value.
(KnownSymbol name, HasField name model value) =>
Proxy name -> model -> value
get IsLabel "name" (Proxy "name")
Proxy "name"
#name (CreateTable -> Text)
-> (Statement -> CreateTable) -> Statement -> Text
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Statement -> CreateTable
unsafeGetCreateTable) ([Statement]
statements [Statement] -> Int -> Statement
forall a. [a] -> Int -> a
!! Int
tableId)
        let validationResult :: ValidatorResult
validationResult = Text
tableName Text -> (Text -> ValidatorResult) -> ValidatorResult
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> [Statement] -> Maybe Text -> Text -> ValidatorResult
validateTable [Statement]
statements (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
oldTableName)
        case ValidatorResult
validationResult of
            Failure Text
message -> do
                (?context::ControllerContext) => Text -> IO ()
Text -> IO ()
setErrorMessage Text
message
                TablesController -> IO ()
forall action.
(?context::ControllerContext, HasPath action) =>
action -> IO ()
redirectTo ShowTableAction :: Text -> TablesController
ShowTableAction { $sel:tableName:TablesAction :: Text
tableName = Text
oldTableName }
            ValidatorResult
Success -> do
                ([Statement] -> [Statement]) -> IO ()
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
([Statement] -> [Statement]) -> IO ()
updateSchema (Int -> Text -> [Statement] -> [Statement]
updateTable Int
tableId Text
tableName)
                TablesController -> IO ()
forall action.
(?context::ControllerContext, HasPath action) =>
action -> IO ()
redirectTo ShowTableAction :: Text -> TablesController
ShowTableAction { Text
tableName :: Text
$sel:tableName:TablesAction :: Text
.. }

    action DeleteTableAction { Int
Text
tableName :: Text
tableId :: Int
$sel:tableId:TablesAction :: TablesController -> Int
$sel:tableName:TablesAction :: TablesController -> Text
.. } = do
        let tableId :: Int
tableId = ByteString -> Int
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"tableId"
        let tableName :: Text
tableName = ByteString -> Text
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"tableName"
        ([Statement] -> [Statement]) -> IO ()
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
([Statement] -> [Statement]) -> IO ()
updateSchema (Int -> [Statement] -> [Statement]
deleteTable Int
tableId)
        ([Statement] -> [Statement]) -> IO ()
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
([Statement] -> [Statement]) -> IO ()
updateSchema (Text -> [Statement] -> [Statement]
deleteForeignKeyConstraints Text
tableName)
        TablesController -> IO ()
forall action.
(?context::ControllerContext, HasPath action) =>
action -> IO ()
redirectTo TablesController
TablesAction


addTable :: Text -> [Statement] -> [Statement]
addTable :: Text -> [Statement] -> [Statement]
addTable Text
tableName [Statement]
list = [Statement]
list [Statement] -> [Statement] -> [Statement]
forall a. Semigroup a => a -> a -> a
<> [CreateTable -> Statement
StatementCreateTable CreateTable :: Text
-> [Column] -> PrimaryKeyConstraint -> [Constraint] -> CreateTable
CreateTable
    { $sel:name:CreateTable :: Text
name = Text
tableName
    , $sel:columns:CreateTable :: [Column]
columns =
        [Column :: Text -> PostgresType -> Maybe Expression -> Bool -> Bool -> Column
Column
            { $sel:name:Column :: Text
name = Text
"id"
            , $sel:columnType:Column :: PostgresType
columnType = PostgresType
PUUID
            , $sel:defaultValue:Column :: Maybe Expression
defaultValue = Expression -> Maybe Expression
forall a. a -> Maybe a
Just (Text -> [Expression] -> Expression
CallExpression Text
"uuid_generate_v4" [])
            , $sel:notNull:Column :: Bool
notNull = Bool
True
            , $sel:isUnique:Column :: Bool
isUnique = Bool
False
            }]
    , $sel:primaryKeyConstraint:CreateTable :: PrimaryKeyConstraint
primaryKeyConstraint = [Text] -> PrimaryKeyConstraint
PrimaryKeyConstraint [Text
"id"]
    , $sel:constraints:CreateTable :: [Constraint]
constraints = []
    }]

updateTable :: Int -> Text -> [Statement] -> [Statement]
updateTable :: Int -> Text -> [Statement] -> [Statement]
updateTable Int
tableId Text
tableName [Statement]
list = Int -> Statement -> [Statement] -> [Statement]
forall a. Int -> a -> [a] -> [a]
replace Int
tableId (CreateTable -> Statement
StatementCreateTable CreateTable :: Text
-> [Column] -> PrimaryKeyConstraint -> [Constraint] -> CreateTable
CreateTable { $sel:name:CreateTable :: Text
name = Text
tableName, $sel:columns:CreateTable :: [Column]
columns = Proxy "columns" -> CreateTable -> [Column]
forall model (name :: Symbol) value.
(KnownSymbol name, HasField name model value) =>
Proxy name -> model -> value
get IsLabel "columns" (Proxy "columns")
Proxy "columns"
#columns CreateTable
table, $sel:primaryKeyConstraint:CreateTable :: PrimaryKeyConstraint
primaryKeyConstraint = Proxy "primaryKeyConstraint" -> CreateTable -> PrimaryKeyConstraint
forall model (name :: Symbol) value.
(KnownSymbol name, HasField name model value) =>
Proxy name -> model -> value
get IsLabel "primaryKeyConstraint" (Proxy "primaryKeyConstraint")
Proxy "primaryKeyConstraint"
#primaryKeyConstraint CreateTable
table, $sel:constraints:CreateTable :: [Constraint]
constraints = Proxy "constraints" -> CreateTable -> [Constraint]
forall model (name :: Symbol) value.
(KnownSymbol name, HasField name model value) =>
Proxy name -> model -> value
get IsLabel "constraints" (Proxy "constraints")
Proxy "constraints"
#constraints CreateTable
table }) [Statement]
list
  where table :: CreateTable
table = Statement -> CreateTable
unsafeGetCreateTable ([Statement]
list [Statement] -> Int -> Statement
forall a. [a] -> Int -> a
!! Int
tableId)

deleteTable :: Int -> [Statement] -> [Statement]
deleteTable :: Int -> [Statement] -> [Statement]
deleteTable Int
tableId [Statement]
list = Statement -> [Statement] -> [Statement]
forall a. Eq a => a -> [a] -> [a]
delete ([Statement]
list [Statement] -> Int -> Statement
forall a. [a] -> Int -> a
!! Int
tableId) [Statement]
list

deleteForeignKeyConstraints :: Text -> [Statement] -> [Statement]
deleteForeignKeyConstraints :: Text -> [Statement] -> [Statement]
deleteForeignKeyConstraints Text
tableName [Statement]
list = (Statement -> Bool) -> [Statement] -> [Statement]
forall a. (a -> Bool) -> [a] -> [a]
filter (\Statement
con -> Bool -> Bool
not (Statement
con Statement -> Statement -> Bool
forall a. Eq a => a -> a -> Bool
== AddConstraint :: Text -> Text -> Constraint -> Statement
AddConstraint { $sel:tableName:StatementCreateTable :: Text
tableName = Text
tableName, $sel:constraintName:StatementCreateTable :: Text
constraintName = Proxy "constraintName" -> Statement -> Text
forall model (name :: Symbol) value.
(KnownSymbol name, HasField name model value) =>
Proxy name -> model -> value
get IsLabel "constraintName" (Proxy "constraintName")
Proxy "constraintName"
#constraintName Statement
con, $sel:constraint:StatementCreateTable :: Constraint
constraint = Proxy "constraint" -> Statement -> Constraint
forall model (name :: Symbol) value.
(KnownSymbol name, HasField name model value) =>
Proxy name -> model -> value
get IsLabel "constraint" (Proxy "constraint")
Proxy "constraint"
#constraint Statement
con })) [Statement]
list

validateTable :: [Statement] -> Maybe Text -> Validator Text
validateTable :: [Statement] -> Maybe Text -> Text -> ValidatorResult
validateTable [Statement]
statements = Text -> [Text] -> Maybe Text -> Text -> ValidatorResult
validateNameInSchema Text
"table name" ([Statement] -> [Text]
getAllObjectNames [Statement]
statements)