-- | Helper functions for generating the schema of database tables
module Hasura.GraphQL.Schema.Table
  ( getTableGQLName,
    tableSelectColumnsEnum,
    tableSelectColumnsPredEnum,
    tableUpdateColumnsEnum,
    updateColumnsPlaceholderParser,
    tableSelectPermissions,
    tableSelectFields,
    tableColumns,
    tableSelectColumns,
    tableUpdateColumns,
    getTableIdentifierName,
  )
where

import Data.Has
import Data.HashMap.Strict qualified as Map
import Data.HashSet qualified as Set
import Data.Text (pack)
import Data.Text.Casing qualified as C
import Data.Text.Extended
import Hasura.Base.Error (QErr)
import Hasura.GraphQL.Schema.Backend
import Hasura.GraphQL.Schema.Common
import Hasura.GraphQL.Schema.Parser (Kind (..), Parser)
import Hasura.GraphQL.Schema.Parser qualified as P
import Hasura.GraphQL.Schema.Typename (mkTypename)
import Hasura.Name qualified as Name
import Hasura.Prelude
import Hasura.RQL.Types.Backend
import Hasura.RQL.Types.Column
import Hasura.RQL.Types.ComputedField
import Hasura.RQL.Types.Relationships.Local
import Hasura.RQL.Types.SchemaCache hiding (askTableInfo)
import Hasura.RQL.Types.Source
import Hasura.RQL.Types.SourceCustomization (applyTypeNameCaseIdentifier, mkTableSelectColumnTypeName, mkTableUpdateColumnTypeName)
import Hasura.RQL.Types.Table
import Hasura.Session (RoleName)
import Language.GraphQL.Draft.Syntax qualified as G

-- | Helper function to get the table GraphQL name. A table may have a
-- custom name configured with it. When the custom name exists, the GraphQL nodes
-- that are generated according to the custom name. For example: Let's say,
-- we have a table called `users address`, the name of the table is not GraphQL
-- compliant so we configure the table with a GraphQL compliant name,
-- say `users_address`
-- The generated top-level nodes of this table will be like `users_address`,
-- `insert_users_address` etc
getTableGQLName ::
  forall b m.
  (Backend b, MonadError QErr m) =>
  TableInfo b ->
  m G.Name
getTableGQLName :: TableInfo b -> m Name
getTableGQLName TableInfo b
tableInfo = do
  let coreInfo :: TableCoreInfo b
coreInfo = TableInfo b -> TableCoreInfo b
forall (b :: BackendType). TableInfo b -> TableCoreInfo b
_tiCoreInfo TableInfo b
tableInfo
      tableName :: TableName b
tableName = TableCoreInfo b -> TableName b
forall (b :: BackendType) field primaryKeyColumn.
TableCoreInfoG b field primaryKeyColumn -> TableName b
_tciName TableCoreInfo b
coreInfo
      tableCustomName :: Maybe Name
tableCustomName = TableConfig b -> Maybe Name
forall (b :: BackendType). TableConfig b -> Maybe Name
_tcCustomName (TableConfig b -> Maybe Name) -> TableConfig b -> Maybe Name
forall a b. (a -> b) -> a -> b
$ TableCoreInfo b -> TableConfig b
forall (b :: BackendType) field primaryKeyColumn.
TableCoreInfoG b field primaryKeyColumn -> TableConfig b
_tciCustomConfig TableCoreInfo b
coreInfo
  Maybe Name
tableCustomName
    Maybe Name -> Either QErr Name -> Either QErr Name
forall (m :: * -> *) a. Applicative m => Maybe a -> m a -> m a
`onNothing` TableName b -> Either QErr Name
forall (b :: BackendType).
Backend b =>
TableName b -> Either QErr Name
tableGraphQLName @b TableName b
tableName
    Either QErr Name -> (QErr -> m Name) -> m Name
forall (m :: * -> *) e a.
Applicative m =>
Either e a -> (e -> m a) -> m a
`onLeft` QErr -> m Name
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError

-- | similar to @getTableGQLName@ but returns table name as a list with name pieces
--   instead of concatenating schema and table name together.
getTableIdentifierName ::
  forall b m.
  (Backend b, MonadError QErr m) =>
  TableInfo b ->
  m (C.GQLNameIdentifier)
getTableIdentifierName :: TableInfo b -> m GQLNameIdentifier
getTableIdentifierName TableInfo b
tableInfo =
  let coreInfo :: TableCoreInfo b
coreInfo = TableInfo b -> TableCoreInfo b
forall (b :: BackendType). TableInfo b -> TableCoreInfo b
_tiCoreInfo TableInfo b
tableInfo
      tableName :: TableName b
tableName = TableCoreInfo b -> TableName b
forall (b :: BackendType) field primaryKeyColumn.
TableCoreInfoG b field primaryKeyColumn -> TableName b
_tciName TableCoreInfo b
coreInfo
      tableCustomName :: Maybe GQLNameIdentifier
tableCustomName = (Name -> GQLNameIdentifier)
-> Maybe Name -> Maybe GQLNameIdentifier
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Name -> GQLNameIdentifier
C.fromCustomName (Maybe Name -> Maybe GQLNameIdentifier)
-> Maybe Name -> Maybe GQLNameIdentifier
forall a b. (a -> b) -> a -> b
$ TableConfig b -> Maybe Name
forall (b :: BackendType). TableConfig b -> Maybe Name
_tcCustomName (TableConfig b -> Maybe Name) -> TableConfig b -> Maybe Name
forall a b. (a -> b) -> a -> b
$ TableCoreInfo b -> TableConfig b
forall (b :: BackendType) field primaryKeyColumn.
TableCoreInfoG b field primaryKeyColumn -> TableConfig b
_tciCustomConfig TableCoreInfo b
coreInfo
   in Maybe GQLNameIdentifier
-> m GQLNameIdentifier -> m GQLNameIdentifier
forall (m :: * -> *) a. Applicative m => Maybe a -> m a -> m a
onNothing
        Maybe GQLNameIdentifier
tableCustomName
        (Either QErr GQLNameIdentifier -> m GQLNameIdentifier
forall e (m :: * -> *) a. MonadError e m => Either e a -> m a
liftEither (Either QErr GQLNameIdentifier -> m GQLNameIdentifier)
-> Either QErr GQLNameIdentifier -> m GQLNameIdentifier
forall a b. (a -> b) -> a -> b
$ TableName b -> Either QErr GQLNameIdentifier
forall (b :: BackendType).
Backend b =>
TableName b -> Either QErr GQLNameIdentifier
getTableIdentifier @b TableName b
tableName)

-- | Table select columns enum
--
-- Parser for an enum type that matches the columns of the given
-- table. Used as a parameter for "distinct", among others. Maps to
-- the table_select_column object.
--
-- Return Nothing if there's no column the current user has "select"
-- permissions for.
tableSelectColumnsEnum ::
  forall b r m n.
  (Backend b, MonadBuildSchemaBase r m n) =>
  SourceInfo b ->
  TableInfo b ->
  m (Maybe (Parser 'Both n (Column b)))
tableSelectColumnsEnum :: SourceInfo b
-> TableInfo b -> m (Maybe (Parser 'Both n (Column b)))
tableSelectColumnsEnum SourceInfo b
sourceInfo TableInfo b
tableInfo = do
  NamingCase
tCase <- (r -> NamingCase) -> m NamingCase
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks r -> NamingCase
forall a t. Has a t => t -> a
getter
  GQLNameIdentifier
tableGQLName <- TableInfo b -> m GQLNameIdentifier
forall (b :: BackendType) (m :: * -> *).
(Backend b, MonadError QErr m) =>
TableInfo b -> m GQLNameIdentifier
getTableIdentifierName @b TableInfo b
tableInfo
  [ColumnInfo b]
columns <- SourceInfo b -> TableInfo b -> m [ColumnInfo b]
forall (b :: BackendType) r (m :: * -> *).
(Backend b, MonadError QErr m, MonadReader r m,
 Has SchemaContext r) =>
SourceInfo b -> TableInfo b -> m [ColumnInfo b]
tableSelectColumns SourceInfo b
sourceInfo TableInfo b
tableInfo
  Name
enumName <- Name -> m Name
forall r (m :: * -> *).
(MonadReader r m, Has MkTypename r) =>
Name -> m Name
mkTypename (Name -> m Name) -> Name -> m Name
forall a b. (a -> b) -> a -> b
$ NamingCase -> GQLNameIdentifier -> Name
applyTypeNameCaseIdentifier NamingCase
tCase (GQLNameIdentifier -> Name) -> GQLNameIdentifier -> Name
forall a b. (a -> b) -> a -> b
$ GQLNameIdentifier -> GQLNameIdentifier
mkTableSelectColumnTypeName GQLNameIdentifier
tableGQLName
  let description :: Maybe Description
description =
        Description -> Maybe Description
forall a. a -> Maybe a
Just (Description -> Maybe Description)
-> Description -> Maybe Description
forall a b. (a -> b) -> a -> b
$
          Text -> Description
G.Description (Text -> Description) -> Text -> Description
forall a b. (a -> b) -> a -> b
$
            Text
"select columns of table " Text -> TableName b -> Text
forall t. ToTxt t => Text -> t -> Text
<>> TableInfo b -> TableName b
forall (b :: BackendType). TableInfo b -> TableName b
tableInfoName TableInfo b
tableInfo
  Maybe (Parser 'Both n (Column b))
-> m (Maybe (Parser 'Both n (Column b)))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe (Parser 'Both n (Column b))
 -> m (Maybe (Parser 'Both n (Column b))))
-> Maybe (Parser 'Both n (Column b))
-> m (Maybe (Parser 'Both n (Column b)))
forall a b. (a -> b) -> a -> b
$
    Name
-> Maybe Description
-> NonEmpty (Definition MetadataObjId EnumValueInfo, Column b)
-> Parser 'Both n (Column b)
forall (m :: * -> *) origin a.
MonadParse m =>
Name
-> Maybe Description
-> NonEmpty (Definition origin EnumValueInfo, a)
-> Parser origin 'Both m a
P.enum Name
enumName Maybe Description
description
      (NonEmpty (Definition MetadataObjId EnumValueInfo, Column b)
 -> Parser 'Both n (Column b))
-> Maybe
     (NonEmpty (Definition MetadataObjId EnumValueInfo, Column b))
-> Maybe (Parser 'Both n (Column b))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Definition MetadataObjId EnumValueInfo, Column b)]
-> Maybe
     (NonEmpty (Definition MetadataObjId EnumValueInfo, Column b))
forall a. [a] -> Maybe (NonEmpty a)
nonEmpty
        [ ( Name -> Definition MetadataObjId EnumValueInfo
forall origin. Name -> Definition origin EnumValueInfo
define (Name -> Definition MetadataObjId EnumValueInfo)
-> Name -> Definition MetadataObjId EnumValueInfo
forall a b. (a -> b) -> a -> b
$ ColumnInfo b -> Name
forall (b :: BackendType). ColumnInfo b -> Name
ciName ColumnInfo b
column,
            ColumnInfo b -> Column b
forall (b :: BackendType). ColumnInfo b -> Column b
ciColumn ColumnInfo b
column
          )
          | ColumnInfo b
column <- [ColumnInfo b]
columns
        ]
  where
    define :: Name -> Definition origin EnumValueInfo
define Name
name =
      Name
-> Maybe Description
-> Maybe origin
-> [Directive Void]
-> EnumValueInfo
-> Definition origin EnumValueInfo
forall origin a.
Name
-> Maybe Description
-> Maybe origin
-> [Directive Void]
-> a
-> Definition origin a
P.Definition Name
name (Description -> Maybe Description
forall a. a -> Maybe a
Just (Description -> Maybe Description)
-> Description -> Maybe Description
forall a b. (a -> b) -> a -> b
$ Text -> Description
G.Description Text
"column name") Maybe origin
forall a. Maybe a
Nothing [] EnumValueInfo
P.EnumValueInfo

-- | Table select columns enum of a certain type.
--
-- Parser for an enum type that matches, of a given table, certain columns which
-- satisfy a predicate.  Used as a parameter for aggregation predicate
-- arguments, among others. Maps to the table_select_column object.
--
-- Return Nothing if there's no column the current user has "select"
-- permissions for.
tableSelectColumnsPredEnum ::
  forall b r m n.
  MonadBuildSchema b r m n =>
  (ColumnType b -> Bool) ->
  G.Name ->
  SourceInfo b ->
  TableInfo b ->
  m (Maybe (Parser 'Both n (Column b)))
tableSelectColumnsPredEnum :: (ColumnType b -> Bool)
-> Name
-> SourceInfo b
-> TableInfo b
-> m (Maybe (Parser 'Both n (Column b)))
tableSelectColumnsPredEnum ColumnType b -> Bool
columnPredicate Name
predName SourceInfo b
sourceInfo TableInfo b
tableInfo = do
  Name
tableGQLName <- TableInfo b -> m Name
forall (b :: BackendType) (m :: * -> *).
(Backend b, MonadError QErr m) =>
TableInfo b -> m Name
getTableGQLName @b TableInfo b
tableInfo
  [ColumnInfo b]
columns <- (ColumnInfo b -> Bool) -> [ColumnInfo b] -> [ColumnInfo b]
forall a. (a -> Bool) -> [a] -> [a]
filter (ColumnType b -> Bool
columnPredicate (ColumnType b -> Bool)
-> (ColumnInfo b -> ColumnType b) -> ColumnInfo b -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ColumnInfo b -> ColumnType b
forall (b :: BackendType). ColumnInfo b -> ColumnType b
ciType) ([ColumnInfo b] -> [ColumnInfo b])
-> m [ColumnInfo b] -> m [ColumnInfo b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SourceInfo b -> TableInfo b -> m [ColumnInfo b]
forall (b :: BackendType) r (m :: * -> *).
(Backend b, MonadError QErr m, MonadReader r m,
 Has SchemaContext r) =>
SourceInfo b -> TableInfo b -> m [ColumnInfo b]
tableSelectColumns SourceInfo b
sourceInfo TableInfo b
tableInfo
  Name
enumName <- Name -> m Name
forall r (m :: * -> *).
(MonadReader r m, Has MkTypename r) =>
Name -> m Name
mkTypename (Name -> m Name) -> Name -> m Name
forall a b. (a -> b) -> a -> b
$ Name
tableGQLName Name -> Name -> Name
forall a. Semigroup a => a -> a -> a
<> Name
Name.__select_column Name -> Name -> Name
forall a. Semigroup a => a -> a -> a
<> Name
Name.__ Name -> Name -> Name
forall a. Semigroup a => a -> a -> a
<> Name
predName
  let description :: Maybe Description
description =
        Description -> Maybe Description
forall a. a -> Maybe a
Just (Description -> Maybe Description)
-> Description -> Maybe Description
forall a b. (a -> b) -> a -> b
$
          Text -> Description
G.Description (Text -> Description) -> Text -> Description
forall a b. (a -> b) -> a -> b
$
            String -> Text
pack (String
"select " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Name -> String
forall a. Show a => a -> String
show Name
predName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"columns of table ") Text -> TableName b -> Text
forall t. ToTxt t => Text -> t -> Text
<>> TableInfo b -> TableName b
forall (b :: BackendType). TableInfo b -> TableName b
tableInfoName TableInfo b
tableInfo
  Maybe (Parser 'Both n (Column b))
-> m (Maybe (Parser 'Both n (Column b)))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe (Parser 'Both n (Column b))
 -> m (Maybe (Parser 'Both n (Column b))))
-> Maybe (Parser 'Both n (Column b))
-> m (Maybe (Parser 'Both n (Column b)))
forall a b. (a -> b) -> a -> b
$
    Name
-> Maybe Description
-> NonEmpty (Definition MetadataObjId EnumValueInfo, Column b)
-> Parser 'Both n (Column b)
forall (m :: * -> *) origin a.
MonadParse m =>
Name
-> Maybe Description
-> NonEmpty (Definition origin EnumValueInfo, a)
-> Parser origin 'Both m a
P.enum Name
enumName Maybe Description
description
      (NonEmpty (Definition MetadataObjId EnumValueInfo, Column b)
 -> Parser 'Both n (Column b))
-> Maybe
     (NonEmpty (Definition MetadataObjId EnumValueInfo, Column b))
-> Maybe (Parser 'Both n (Column b))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Definition MetadataObjId EnumValueInfo, Column b)]
-> Maybe
     (NonEmpty (Definition MetadataObjId EnumValueInfo, Column b))
forall a. [a] -> Maybe (NonEmpty a)
nonEmpty
        [ ( Name -> Definition MetadataObjId EnumValueInfo
forall origin. Name -> Definition origin EnumValueInfo
define (Name -> Definition MetadataObjId EnumValueInfo)
-> Name -> Definition MetadataObjId EnumValueInfo
forall a b. (a -> b) -> a -> b
$ ColumnInfo b -> Name
forall (b :: BackendType). ColumnInfo b -> Name
ciName ColumnInfo b
column,
            ColumnInfo b -> Column b
forall (b :: BackendType). ColumnInfo b -> Column b
ciColumn ColumnInfo b
column
          )
          | ColumnInfo b
column <- [ColumnInfo b]
columns
        ]
  where
    define :: Name -> Definition origin EnumValueInfo
define Name
name =
      Name
-> Maybe Description
-> Maybe origin
-> [Directive Void]
-> EnumValueInfo
-> Definition origin EnumValueInfo
forall origin a.
Name
-> Maybe Description
-> Maybe origin
-> [Directive Void]
-> a
-> Definition origin a
P.Definition Name
name (Description -> Maybe Description
forall a. a -> Maybe a
Just (Description -> Maybe Description)
-> Description -> Maybe Description
forall a b. (a -> b) -> a -> b
$ Text -> Description
G.Description Text
"column name") Maybe origin
forall a. Maybe a
Nothing [] EnumValueInfo
P.EnumValueInfo

-- | Table update columns enum
--
-- Parser for an enum type that matches the columns of the given
-- table. Used for conflict resolution in "insert" mutations, among
-- others. Maps to the table_update_column object.
tableUpdateColumnsEnum ::
  forall b r m n.
  MonadBuildSchema b r m n =>
  TableInfo b ->
  m (Maybe (Parser 'Both n (Column b)))
tableUpdateColumnsEnum :: TableInfo b -> m (Maybe (Parser 'Both n (Column b)))
tableUpdateColumnsEnum TableInfo b
tableInfo = do
  RoleName
roleName <- (SchemaContext -> RoleName) -> m RoleName
forall r (m :: * -> *) a b.
(MonadReader r m, Has a r) =>
(a -> b) -> m b
retrieve SchemaContext -> RoleName
scRole
  NamingCase
tCase <- (r -> NamingCase) -> m NamingCase
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks r -> NamingCase
forall a t. Has a t => t -> a
getter
  GQLNameIdentifier
tableGQLName <- TableInfo b -> m GQLNameIdentifier
forall (b :: BackendType) (m :: * -> *).
(Backend b, MonadError QErr m) =>
TableInfo b -> m GQLNameIdentifier
getTableIdentifierName TableInfo b
tableInfo
  Name
enumName <- Name -> m Name
forall r (m :: * -> *).
(MonadReader r m, Has MkTypename r) =>
Name -> m Name
mkTypename (Name -> m Name) -> Name -> m Name
forall a b. (a -> b) -> a -> b
$ NamingCase -> GQLNameIdentifier -> Name
applyTypeNameCaseIdentifier NamingCase
tCase (GQLNameIdentifier -> Name) -> GQLNameIdentifier -> Name
forall a b. (a -> b) -> a -> b
$ GQLNameIdentifier -> GQLNameIdentifier
mkTableUpdateColumnTypeName GQLNameIdentifier
tableGQLName
  let tableName :: TableName b
tableName = TableInfo b -> TableName b
forall (b :: BackendType). TableInfo b -> TableName b
tableInfoName TableInfo b
tableInfo
      enumDesc :: Maybe Description
enumDesc = Description -> Maybe Description
forall a. a -> Maybe a
Just (Description -> Maybe Description)
-> Description -> Maybe Description
forall a b. (a -> b) -> a -> b
$ Text -> Description
G.Description (Text -> Description) -> Text -> Description
forall a b. (a -> b) -> a -> b
$ Text
"update columns of table " Text -> TableName b -> Text
forall t. ToTxt t => Text -> t -> Text
<>> TableName b
tableName
      enumValues :: [(Definition MetadataObjId EnumValueInfo, Column b)]
enumValues = do
        ColumnInfo b
column <- RoleName -> TableInfo b -> [ColumnInfo b]
forall (b :: BackendType).
Backend b =>
RoleName -> TableInfo b -> [ColumnInfo b]
tableUpdateColumns RoleName
roleName TableInfo b
tableInfo
        (Definition MetadataObjId EnumValueInfo, Column b)
-> [(Definition MetadataObjId EnumValueInfo, Column b)]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Name -> Definition MetadataObjId EnumValueInfo
forall origin. Name -> Definition origin EnumValueInfo
define (Name -> Definition MetadataObjId EnumValueInfo)
-> Name -> Definition MetadataObjId EnumValueInfo
forall a b. (a -> b) -> a -> b
$ ColumnInfo b -> Name
forall (b :: BackendType). ColumnInfo b -> Name
ciName ColumnInfo b
column, ColumnInfo b -> Column b
forall (b :: BackendType). ColumnInfo b -> Column b
ciColumn ColumnInfo b
column)
  Maybe (Parser 'Both n (Column b))
-> m (Maybe (Parser 'Both n (Column b)))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe (Parser 'Both n (Column b))
 -> m (Maybe (Parser 'Both n (Column b))))
-> Maybe (Parser 'Both n (Column b))
-> m (Maybe (Parser 'Both n (Column b)))
forall a b. (a -> b) -> a -> b
$ Name
-> Maybe Description
-> NonEmpty (Definition MetadataObjId EnumValueInfo, Column b)
-> Parser 'Both n (Column b)
forall (m :: * -> *) origin a.
MonadParse m =>
Name
-> Maybe Description
-> NonEmpty (Definition origin EnumValueInfo, a)
-> Parser origin 'Both m a
P.enum Name
enumName Maybe Description
enumDesc (NonEmpty (Definition MetadataObjId EnumValueInfo, Column b)
 -> Parser 'Both n (Column b))
-> Maybe
     (NonEmpty (Definition MetadataObjId EnumValueInfo, Column b))
-> Maybe (Parser 'Both n (Column b))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Definition MetadataObjId EnumValueInfo, Column b)]
-> Maybe
     (NonEmpty (Definition MetadataObjId EnumValueInfo, Column b))
forall a. [a] -> Maybe (NonEmpty a)
nonEmpty [(Definition MetadataObjId EnumValueInfo, Column b)]
enumValues
  where
    define :: Name -> Definition origin EnumValueInfo
define Name
name = Name
-> Maybe Description
-> Maybe origin
-> [Directive Void]
-> EnumValueInfo
-> Definition origin EnumValueInfo
forall origin a.
Name
-> Maybe Description
-> Maybe origin
-> [Directive Void]
-> a
-> Definition origin a
P.Definition Name
name (Description -> Maybe Description
forall a. a -> Maybe a
Just (Description -> Maybe Description)
-> Description -> Maybe Description
forall a b. (a -> b) -> a -> b
$ Text -> Description
G.Description Text
"column name") Maybe origin
forall a. Maybe a
Nothing [] EnumValueInfo
P.EnumValueInfo

-- If there's no column for which the current user has "update"
-- permissions, this functions returns an enum that only contains a
-- placeholder, so as to still allow this type to exist in the schema.
updateColumnsPlaceholderParser ::
  MonadBuildSchema backend r m n =>
  TableInfo backend ->
  m (Parser 'Both n (Maybe (Column backend)))
updateColumnsPlaceholderParser :: TableInfo backend -> m (Parser 'Both n (Maybe (Column backend)))
updateColumnsPlaceholderParser TableInfo backend
tableInfo = do
  NamingCase
tCase <- (r -> NamingCase) -> m NamingCase
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks r -> NamingCase
forall a t. Has a t => t -> a
getter
  Maybe (Parser MetadataObjId 'Both n (Column backend))
maybeEnum <- TableInfo backend
-> m (Maybe (Parser MetadataObjId 'Both n (Column backend)))
forall (b :: BackendType) r (m :: * -> *) (n :: * -> *).
MonadBuildSchema b r m n =>
TableInfo b -> m (Maybe (Parser 'Both n (Column b)))
tableUpdateColumnsEnum TableInfo backend
tableInfo
  case Maybe (Parser MetadataObjId 'Both n (Column backend))
maybeEnum of
    Just Parser MetadataObjId 'Both n (Column backend)
e -> Parser 'Both n (Maybe (Column backend))
-> m (Parser 'Both n (Maybe (Column backend)))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Parser 'Both n (Maybe (Column backend))
 -> m (Parser 'Both n (Maybe (Column backend))))
-> Parser 'Both n (Maybe (Column backend))
-> m (Parser 'Both n (Maybe (Column backend)))
forall a b. (a -> b) -> a -> b
$ Column backend -> Maybe (Column backend)
forall a. a -> Maybe a
Just (Column backend -> Maybe (Column backend))
-> Parser MetadataObjId 'Both n (Column backend)
-> Parser 'Both n (Maybe (Column backend))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser MetadataObjId 'Both n (Column backend)
e
    Maybe (Parser MetadataObjId 'Both n (Column backend))
Nothing -> do
      GQLNameIdentifier
tableGQLName <- TableInfo backend -> m GQLNameIdentifier
forall (b :: BackendType) (m :: * -> *).
(Backend b, MonadError QErr m) =>
TableInfo b -> m GQLNameIdentifier
getTableIdentifierName TableInfo backend
tableInfo
      Name
enumName <- Name -> m Name
forall r (m :: * -> *).
(MonadReader r m, Has MkTypename r) =>
Name -> m Name
mkTypename (Name -> m Name) -> Name -> m Name
forall a b. (a -> b) -> a -> b
$ NamingCase -> GQLNameIdentifier -> Name
applyTypeNameCaseIdentifier NamingCase
tCase (GQLNameIdentifier -> Name) -> GQLNameIdentifier -> Name
forall a b. (a -> b) -> a -> b
$ GQLNameIdentifier -> GQLNameIdentifier
mkTableUpdateColumnTypeName GQLNameIdentifier
tableGQLName
      Parser 'Both n (Maybe (Column backend))
-> m (Parser 'Both n (Maybe (Column backend)))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Parser 'Both n (Maybe (Column backend))
 -> m (Parser 'Both n (Maybe (Column backend))))
-> Parser 'Both n (Maybe (Column backend))
-> m (Parser 'Both n (Maybe (Column backend)))
forall a b. (a -> b) -> a -> b
$
        Name
-> Maybe Description
-> NonEmpty
     (Definition MetadataObjId EnumValueInfo, Maybe (Column backend))
-> Parser 'Both n (Maybe (Column backend))
forall (m :: * -> *) origin a.
MonadParse m =>
Name
-> Maybe Description
-> NonEmpty (Definition origin EnumValueInfo, a)
-> Parser origin 'Both m a
P.enum Name
enumName (Description -> Maybe Description
forall a. a -> Maybe a
Just (Description -> Maybe Description)
-> Description -> Maybe Description
forall a b. (a -> b) -> a -> b
$ Text -> Description
G.Description (Text -> Description) -> Text -> Description
forall a b. (a -> b) -> a -> b
$ Text
"placeholder for update columns of table " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> TableInfo backend -> TableName backend
forall (b :: BackendType). TableInfo b -> TableName b
tableInfoName TableInfo backend
tableInfo TableName backend -> Text -> Text
forall t. ToTxt t => t -> Text -> Text
<<> Text
" (current role has no relevant permissions)") (NonEmpty
   (Definition MetadataObjId EnumValueInfo, Maybe (Column backend))
 -> Parser 'Both n (Maybe (Column backend)))
-> NonEmpty
     (Definition MetadataObjId EnumValueInfo, Maybe (Column backend))
-> Parser 'Both n (Maybe (Column backend))
forall a b. (a -> b) -> a -> b
$
          (Definition MetadataObjId EnumValueInfo, Maybe (Column backend))
-> NonEmpty
     (Definition MetadataObjId EnumValueInfo, Maybe (Column backend))
forall (f :: * -> *) a. Applicative f => a -> f a
pure
            ( Name
-> Maybe Description
-> Maybe MetadataObjId
-> [Directive Void]
-> EnumValueInfo
-> Definition MetadataObjId EnumValueInfo
forall origin a.
Name
-> Maybe Description
-> Maybe origin
-> [Directive Void]
-> a
-> Definition origin a
P.Definition @_ @P.EnumValueInfo Name
Name.__PLACEHOLDER (Description -> Maybe Description
forall a. a -> Maybe a
Just (Description -> Maybe Description)
-> Description -> Maybe Description
forall a b. (a -> b) -> a -> b
$ Text -> Description
G.Description Text
"placeholder (do not use)") Maybe MetadataObjId
forall a. Maybe a
Nothing [] EnumValueInfo
P.EnumValueInfo,
              Maybe (Column backend)
forall a. Maybe a
Nothing
            )

tableSelectPermissions :: RoleName -> TableInfo b -> Maybe (SelPermInfo b)
tableSelectPermissions :: RoleName -> TableInfo b -> Maybe (SelPermInfo b)
tableSelectPermissions RoleName
role TableInfo b
tableInfo = RolePermInfo b -> Maybe (SelPermInfo b)
forall (b :: BackendType). RolePermInfo b -> Maybe (SelPermInfo b)
_permSel (RolePermInfo b -> Maybe (SelPermInfo b))
-> RolePermInfo b -> Maybe (SelPermInfo b)
forall a b. (a -> b) -> a -> b
$ RoleName -> TableInfo b -> RolePermInfo b
forall (b :: BackendType).
RoleName -> TableInfo b -> RolePermInfo b
getRolePermInfo RoleName
role TableInfo b
tableInfo

tableSelectFields ::
  forall b r m.
  ( Backend b,
    MonadError QErr m,
    MonadReader r m,
    Has SchemaContext r
  ) =>
  SourceInfo b ->
  TableInfo b ->
  m [FieldInfo b]
tableSelectFields :: SourceInfo b -> TableInfo b -> m [FieldInfo b]
tableSelectFields SourceInfo b
sourceInfo TableInfo b
tableInfo = do
  RoleName
roleName <- (SchemaContext -> RoleName) -> m RoleName
forall r (m :: * -> *) a b.
(MonadReader r m, Has a r) =>
(a -> b) -> m b
retrieve SchemaContext -> RoleName
scRole
  let tableFields :: FieldInfoMap (FieldInfo b)
tableFields = TableCoreInfoG b (FieldInfo b) (ColumnInfo b)
-> FieldInfoMap (FieldInfo b)
forall (b :: BackendType) field primaryKeyColumn.
TableCoreInfoG b field primaryKeyColumn -> FieldInfoMap field
_tciFieldInfoMap (TableCoreInfoG b (FieldInfo b) (ColumnInfo b)
 -> FieldInfoMap (FieldInfo b))
-> (TableInfo b -> TableCoreInfoG b (FieldInfo b) (ColumnInfo b))
-> TableInfo b
-> FieldInfoMap (FieldInfo b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TableInfo b -> TableCoreInfoG b (FieldInfo b) (ColumnInfo b)
forall (b :: BackendType). TableInfo b -> TableCoreInfo b
_tiCoreInfo (TableInfo b -> FieldInfoMap (FieldInfo b))
-> TableInfo b -> FieldInfoMap (FieldInfo b)
forall a b. (a -> b) -> a -> b
$ TableInfo b
tableInfo
      permissions :: Maybe (SelPermInfo b)
permissions = RoleName -> TableInfo b -> Maybe (SelPermInfo b)
forall (b :: BackendType).
RoleName -> TableInfo b -> Maybe (SelPermInfo b)
tableSelectPermissions RoleName
roleName TableInfo b
tableInfo
  (FieldInfo b -> m Bool) -> [FieldInfo b] -> m [FieldInfo b]
forall (m :: * -> *) a.
Applicative m =>
(a -> m Bool) -> [a] -> m [a]
filterM (RoleName -> Maybe (SelPermInfo b) -> FieldInfo b -> m Bool
canBeSelected RoleName
roleName Maybe (SelPermInfo b)
permissions) ([FieldInfo b] -> m [FieldInfo b])
-> [FieldInfo b] -> m [FieldInfo b]
forall a b. (a -> b) -> a -> b
$ FieldInfoMap (FieldInfo b) -> [FieldInfo b]
forall k v. HashMap k v -> [v]
Map.elems FieldInfoMap (FieldInfo b)
tableFields
  where
    canBeSelected :: RoleName -> Maybe (SelPermInfo b) -> FieldInfo b -> m Bool
canBeSelected RoleName
_ Maybe (SelPermInfo b)
Nothing FieldInfo b
_ = Bool -> m Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
False
    canBeSelected RoleName
_ (Just SelPermInfo b
permissions) (FIColumn ColumnInfo b
columnInfo) =
      Bool -> m Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool -> m Bool) -> Bool -> m Bool
forall a b. (a -> b) -> a -> b
$ Column b
-> HashMap (Column b) (Maybe (AnnColumnCaseBoolExpPartialSQL b))
-> Bool
forall k a. (Eq k, Hashable k) => k -> HashMap k a -> Bool
Map.member (ColumnInfo b -> Column b
forall (b :: BackendType). ColumnInfo b -> Column b
ciColumn ColumnInfo b
columnInfo) (SelPermInfo b
-> HashMap (Column b) (Maybe (AnnColumnCaseBoolExpPartialSQL b))
forall (b :: BackendType).
SelPermInfo b
-> HashMap (Column b) (Maybe (AnnColumnCaseBoolExpPartialSQL b))
spiCols SelPermInfo b
permissions)
    canBeSelected RoleName
role Maybe (SelPermInfo b)
_ (FIRelationship RelInfo b
relationshipInfo) = do
      TableInfo b
tableInfo' <- SourceInfo b -> TableName b -> m (TableInfo b)
forall (b :: BackendType) (m :: * -> *).
(Backend b, MonadError QErr m) =>
SourceInfo b -> TableName b -> m (TableInfo b)
askTableInfo SourceInfo b
sourceInfo (TableName b -> m (TableInfo b)) -> TableName b -> m (TableInfo b)
forall a b. (a -> b) -> a -> b
$ RelInfo b -> TableName b
forall (b :: BackendType). RelInfo b -> TableName b
riRTable RelInfo b
relationshipInfo
      Bool -> m Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool -> m Bool) -> Bool -> m Bool
forall a b. (a -> b) -> a -> b
$ Maybe (SelPermInfo b) -> Bool
forall a. Maybe a -> Bool
isJust (Maybe (SelPermInfo b) -> Bool) -> Maybe (SelPermInfo b) -> Bool
forall a b. (a -> b) -> a -> b
$ RoleName -> TableInfo b -> Maybe (SelPermInfo b)
forall (b :: BackendType).
RoleName -> TableInfo b -> Maybe (SelPermInfo b)
tableSelectPermissions @b RoleName
role TableInfo b
tableInfo'
    canBeSelected RoleName
role (Just SelPermInfo b
permissions) (FIComputedField ComputedFieldInfo b
computedFieldInfo) =
      case ComputedFieldReturn b -> ComputedFieldReturnType b
forall (b :: BackendType).
Backend b =>
ComputedFieldReturn b -> ComputedFieldReturnType b
computedFieldReturnType @b (ComputedFieldInfo b -> ComputedFieldReturn b
forall (b :: BackendType).
ComputedFieldInfo b -> ComputedFieldReturn b
_cfiReturnType ComputedFieldInfo b
computedFieldInfo) of
        ReturnsScalar ScalarType b
_ ->
          Bool -> m Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool -> m Bool) -> Bool -> m Bool
forall a b. (a -> b) -> a -> b
$ ComputedFieldName
-> HashMap
     ComputedFieldName (Maybe (AnnColumnCaseBoolExpPartialSQL b))
-> Bool
forall k a. (Eq k, Hashable k) => k -> HashMap k a -> Bool
Map.member (ComputedFieldInfo b -> ComputedFieldName
forall (b :: BackendType). ComputedFieldInfo b -> ComputedFieldName
_cfiName ComputedFieldInfo b
computedFieldInfo) (HashMap
   ComputedFieldName (Maybe (AnnColumnCaseBoolExpPartialSQL b))
 -> Bool)
-> HashMap
     ComputedFieldName (Maybe (AnnColumnCaseBoolExpPartialSQL b))
-> Bool
forall a b. (a -> b) -> a -> b
$ SelPermInfo b
-> HashMap
     ComputedFieldName (Maybe (AnnColumnCaseBoolExpPartialSQL b))
forall (b :: BackendType).
SelPermInfo b
-> HashMap
     ComputedFieldName (Maybe (AnnColumnCaseBoolExpPartialSQL b))
spiComputedFields SelPermInfo b
permissions
        ReturnsTable TableName b
tableName -> do
          TableInfo b
tableInfo' <- SourceInfo b -> TableName b -> m (TableInfo b)
forall (b :: BackendType) (m :: * -> *).
(Backend b, MonadError QErr m) =>
SourceInfo b -> TableName b -> m (TableInfo b)
askTableInfo SourceInfo b
sourceInfo TableName b
tableName
          Bool -> m Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool -> m Bool) -> Bool -> m Bool
forall a b. (a -> b) -> a -> b
$ Maybe (SelPermInfo b) -> Bool
forall a. Maybe a -> Bool
isJust (Maybe (SelPermInfo b) -> Bool) -> Maybe (SelPermInfo b) -> Bool
forall a b. (a -> b) -> a -> b
$ RoleName -> TableInfo b -> Maybe (SelPermInfo b)
forall (b :: BackendType).
RoleName -> TableInfo b -> Maybe (SelPermInfo b)
tableSelectPermissions @b RoleName
role TableInfo b
tableInfo'
        ComputedFieldReturnType b
ReturnsOthers -> Bool -> m Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
False
    canBeSelected RoleName
_ Maybe (SelPermInfo b)
_ (FIRemoteRelationship RemoteFieldInfo (DBJoinField b)
_) = Bool -> m Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
True

tableColumns ::
  forall b. TableInfo b -> [ColumnInfo b]
tableColumns :: TableInfo b -> [ColumnInfo b]
tableColumns TableInfo b
tableInfo =
  (FieldInfo b -> Maybe (ColumnInfo b))
-> [FieldInfo b] -> [ColumnInfo b]
forall (f :: * -> *) a b.
Filterable f =>
(a -> Maybe b) -> f a -> f b
mapMaybe FieldInfo b -> Maybe (ColumnInfo b)
forall (b :: BackendType). FieldInfo b -> Maybe (ColumnInfo b)
columnInfo ([FieldInfo b] -> [ColumnInfo b])
-> (TableInfo b -> [FieldInfo b]) -> TableInfo b -> [ColumnInfo b]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HashMap FieldName (FieldInfo b) -> [FieldInfo b]
forall k v. HashMap k v -> [v]
Map.elems (HashMap FieldName (FieldInfo b) -> [FieldInfo b])
-> (TableInfo b -> HashMap FieldName (FieldInfo b))
-> TableInfo b
-> [FieldInfo b]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TableCoreInfoG b (FieldInfo b) (ColumnInfo b)
-> HashMap FieldName (FieldInfo b)
forall (b :: BackendType) field primaryKeyColumn.
TableCoreInfoG b field primaryKeyColumn -> FieldInfoMap field
_tciFieldInfoMap (TableCoreInfoG b (FieldInfo b) (ColumnInfo b)
 -> HashMap FieldName (FieldInfo b))
-> (TableInfo b -> TableCoreInfoG b (FieldInfo b) (ColumnInfo b))
-> TableInfo b
-> HashMap FieldName (FieldInfo b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TableInfo b -> TableCoreInfoG b (FieldInfo b) (ColumnInfo b)
forall (b :: BackendType). TableInfo b -> TableCoreInfo b
_tiCoreInfo (TableInfo b -> [ColumnInfo b]) -> TableInfo b -> [ColumnInfo b]
forall a b. (a -> b) -> a -> b
$ TableInfo b
tableInfo
  where
    columnInfo :: FieldInfo b -> Maybe (ColumnInfo b)
columnInfo (FIColumn ColumnInfo b
ci) = ColumnInfo b -> Maybe (ColumnInfo b)
forall a. a -> Maybe a
Just ColumnInfo b
ci
    columnInfo FieldInfo b
_ = Maybe (ColumnInfo b)
forall a. Maybe a
Nothing

-- | Get the columns of a table that my be selected under the given select
-- permissions.
tableSelectColumns ::
  forall b r m.
  ( Backend b,
    MonadError QErr m,
    MonadReader r m,
    Has SchemaContext r
  ) =>
  SourceInfo b ->
  TableInfo b ->
  m [ColumnInfo b]
tableSelectColumns :: SourceInfo b -> TableInfo b -> m [ColumnInfo b]
tableSelectColumns SourceInfo b
sourceInfo TableInfo b
tableInfo =
  (FieldInfo b -> Maybe (ColumnInfo b))
-> [FieldInfo b] -> [ColumnInfo b]
forall (f :: * -> *) a b.
Filterable f =>
(a -> Maybe b) -> f a -> f b
mapMaybe FieldInfo b -> Maybe (ColumnInfo b)
forall (b :: BackendType). FieldInfo b -> Maybe (ColumnInfo b)
columnInfo ([FieldInfo b] -> [ColumnInfo b])
-> m [FieldInfo b] -> m [ColumnInfo b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SourceInfo b -> TableInfo b -> m [FieldInfo b]
forall (b :: BackendType) r (m :: * -> *).
(Backend b, MonadError QErr m, MonadReader r m,
 Has SchemaContext r) =>
SourceInfo b -> TableInfo b -> m [FieldInfo b]
tableSelectFields SourceInfo b
sourceInfo TableInfo b
tableInfo
  where
    columnInfo :: FieldInfo b -> Maybe (ColumnInfo b)
columnInfo (FIColumn ColumnInfo b
ci) = ColumnInfo b -> Maybe (ColumnInfo b)
forall a. a -> Maybe a
Just ColumnInfo b
ci
    columnInfo FieldInfo b
_ = Maybe (ColumnInfo b)
forall a. Maybe a
Nothing

-- | Get the columns of a table that my be updated under the given update
-- permissions.
tableUpdateColumns ::
  forall b.
  Backend b =>
  RoleName ->
  TableInfo b ->
  [ColumnInfo b]
tableUpdateColumns :: RoleName -> TableInfo b -> [ColumnInfo b]
tableUpdateColumns RoleName
role TableInfo b
tableInfo =
  let permissions :: Maybe (UpdPermInfo b)
permissions = RolePermInfo b -> Maybe (UpdPermInfo b)
forall (b :: BackendType). RolePermInfo b -> Maybe (UpdPermInfo b)
_permUpd (RolePermInfo b -> Maybe (UpdPermInfo b))
-> RolePermInfo b -> Maybe (UpdPermInfo b)
forall a b. (a -> b) -> a -> b
$ RoleName -> TableInfo b -> RolePermInfo b
forall (b :: BackendType).
RoleName -> TableInfo b -> RolePermInfo b
getRolePermInfo RoleName
role TableInfo b
tableInfo
   in (ColumnInfo b -> Bool) -> [ColumnInfo b] -> [ColumnInfo b]
forall a. (a -> Bool) -> [a] -> [a]
filter (Maybe (UpdPermInfo b) -> ColumnInfo b -> Bool
isUpdatable Maybe (UpdPermInfo b)
permissions) ([ColumnInfo b] -> [ColumnInfo b])
-> [ColumnInfo b] -> [ColumnInfo b]
forall a b. (a -> b) -> a -> b
$ TableInfo b -> [ColumnInfo b]
forall (b :: BackendType). TableInfo b -> [ColumnInfo b]
tableColumns TableInfo b
tableInfo
  where
    isUpdatable :: Maybe (UpdPermInfo b) -> ColumnInfo b -> Bool
    isUpdatable :: Maybe (UpdPermInfo b) -> ColumnInfo b -> Bool
isUpdatable (Just UpdPermInfo b
permissions) ColumnInfo b
columnInfo = Bool
columnIsUpdatable Bool -> Bool -> Bool
&& Bool
columnIsPermitted Bool -> Bool -> Bool
&& Bool
columnHasNoPreset
      where
        columnIsUpdatable :: Bool
columnIsUpdatable = ColumnMutability -> Bool
_cmIsUpdatable (ColumnInfo b -> ColumnMutability
forall (b :: BackendType). ColumnInfo b -> ColumnMutability
ciMutability ColumnInfo b
columnInfo)
        columnIsPermitted :: Bool
columnIsPermitted = Column b -> HashSet (Column b) -> Bool
forall a. (Eq a, Hashable a) => a -> HashSet a -> Bool
Set.member (ColumnInfo b -> Column b
forall (b :: BackendType). ColumnInfo b -> Column b
ciColumn ColumnInfo b
columnInfo) (UpdPermInfo b -> HashSet (Column b)
forall (b :: BackendType). UpdPermInfo b -> HashSet (Column b)
upiCols UpdPermInfo b
permissions)
        columnHasNoPreset :: Bool
columnHasNoPreset = Bool -> Bool
not (Column b -> HashMap (Column b) (PartialSQLExp b) -> Bool
forall k a. (Eq k, Hashable k) => k -> HashMap k a -> Bool
Map.member (ColumnInfo b -> Column b
forall (b :: BackendType). ColumnInfo b -> Column b
ciColumn ColumnInfo b
columnInfo) (UpdPermInfo b -> HashMap (Column b) (PartialSQLExp b)
forall (b :: BackendType). UpdPermInfo b -> PreSetColsPartial b
upiSet UpdPermInfo b
permissions))
    isUpdatable Maybe (UpdPermInfo b)
Nothing ColumnInfo b
_ = Bool
False