mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 09:22:43 +03:00
Add schema implementation for Aggregation Predicates
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5259 GitOrigin-RevId: f53e310951fa4eb7570006d8c616398a98078632
This commit is contained in:
parent
967bdcc5d5
commit
4431fb5ea9
@ -815,6 +815,7 @@ library
|
||||
, Hasura.GraphQL.Schema.Action
|
||||
, Hasura.GraphQL.Schema.Backend
|
||||
, Hasura.GraphQL.Schema.BoolExp
|
||||
, Hasura.GraphQL.Schema.BoolExp.AggregationPredicates
|
||||
, Hasura.GraphQL.Schema.Build
|
||||
, Hasura.GraphQL.Schema.Common
|
||||
, Hasura.GraphQL.Schema.Instances
|
||||
|
@ -55,7 +55,9 @@ import Language.GraphQL.Draft.Syntax qualified as G
|
||||
-- @tablename@ defined /and/ these grant non-empty column permissions.
|
||||
ifMatchedFieldParser ::
|
||||
forall r m n.
|
||||
MonadBuildSchema 'MSSQL r m n =>
|
||||
( MonadBuildSchema 'MSSQL r m n,
|
||||
AggregationPredicatesSchema 'MSSQL
|
||||
) =>
|
||||
SourceInfo 'MSSQL ->
|
||||
TableInfo 'MSSQL ->
|
||||
m (InputFieldsParser n (Maybe (IfMatched (UnpreparedValue 'MSSQL))))
|
||||
@ -66,7 +68,9 @@ ifMatchedFieldParser sourceInfo tableInfo = do
|
||||
-- | Parse a @tablename_if_matched@ object.
|
||||
ifMatchedObjectParser ::
|
||||
forall r m n.
|
||||
MonadBuildSchema 'MSSQL r m n =>
|
||||
( MonadBuildSchema 'MSSQL r m n,
|
||||
AggregationPredicatesSchema 'MSSQL
|
||||
) =>
|
||||
SourceInfo 'MSSQL ->
|
||||
TableInfo 'MSSQL ->
|
||||
m (Maybe (Parser 'Input n (IfMatched (UnpreparedValue 'MSSQL))))
|
||||
|
@ -56,7 +56,9 @@ import Language.GraphQL.Draft.Syntax qualified as G
|
||||
-- enum. See <https://github.com/hasura/graphql-engine/issues/6804>.
|
||||
onConflictFieldParser ::
|
||||
forall pgKind r m n.
|
||||
MonadBuildSchema ('Postgres pgKind) r m n =>
|
||||
( MonadBuildSchema ('Postgres pgKind) r m n,
|
||||
AggregationPredicatesSchema ('Postgres pgKind)
|
||||
) =>
|
||||
SourceInfo ('Postgres pgKind) ->
|
||||
TableInfo ('Postgres pgKind) ->
|
||||
m (InputFieldsParser n (Maybe (IR.OnConflictClause ('Postgres pgKind) (IR.UnpreparedValue ('Postgres pgKind)))))
|
||||
@ -73,7 +75,9 @@ onConflictFieldParser sourceInfo tableInfo = do
|
||||
-- | Create a parser for the @_on_conflict@ object of the given table.
|
||||
conflictObjectParser ::
|
||||
forall pgKind r m n.
|
||||
MonadBuildSchema ('Postgres pgKind) r m n =>
|
||||
( MonadBuildSchema ('Postgres pgKind) r m n,
|
||||
AggregationPredicatesSchema ('Postgres pgKind)
|
||||
) =>
|
||||
SourceInfo ('Postgres pgKind) ->
|
||||
TableInfo ('Postgres pgKind) ->
|
||||
Maybe (UpdPermInfo ('Postgres pgKind)) ->
|
||||
|
@ -23,6 +23,7 @@ import Hasura.Backends.Postgres.Types.ComputedField qualified as PG
|
||||
import Hasura.Backends.Postgres.Types.Function qualified as PG
|
||||
import Hasura.Base.Error
|
||||
import Hasura.GraphQL.Schema.Backend
|
||||
import Hasura.GraphQL.Schema.BoolExp
|
||||
import Hasura.GraphQL.Schema.Common
|
||||
import Hasura.GraphQL.Schema.Options qualified as Options
|
||||
import Hasura.GraphQL.Schema.Parser
|
||||
@ -146,6 +147,7 @@ selectFunctionAggregate mkRootFieldName sourceInfo fi@FunctionInfo {..} descript
|
||||
selectFunctionConnection ::
|
||||
forall pgKind r m n.
|
||||
( MonadBuildSchema ('Postgres pgKind) r m n,
|
||||
AggregationPredicatesSchema ('Postgres pgKind),
|
||||
BackendTableSelectSchema ('Postgres pgKind)
|
||||
) =>
|
||||
MkRootFieldName ->
|
||||
@ -194,8 +196,9 @@ selectFunctionConnection mkRootFieldName sourceInfo fi@FunctionInfo {..} descrip
|
||||
-- | Computed field parser
|
||||
computedFieldPG ::
|
||||
forall pgKind r m n.
|
||||
MonadBuildSchema ('Postgres pgKind) r m n =>
|
||||
BackendTableSelectSchema ('Postgres pgKind) =>
|
||||
( MonadBuildSchema ('Postgres pgKind) r m n,
|
||||
BackendTableSelectSchema ('Postgres pgKind)
|
||||
) =>
|
||||
SourceInfo ('Postgres pgKind) ->
|
||||
ComputedFieldInfo ('Postgres pgKind) ->
|
||||
TableName ('Postgres pgKind) ->
|
||||
|
@ -340,7 +340,7 @@ type ComparisonExp b = OpExpG b (UnpreparedValue b)
|
||||
--
|
||||
-- * Pro: You can specify both shared and diverging behavior.
|
||||
-- * Pro: You can specify a lot of behavior implicitly, i.e. it's easy to write.
|
||||
-- * Con: You can specify a lot of behavior implicitly, i.e. it's hard do
|
||||
-- * Con: You can specify a lot of behavior implicitly, i.e. it's hard to
|
||||
-- understand without tracing through implementations.
|
||||
-- * Con: You get a proliferation of type class methods and it's difficult to
|
||||
-- understand how they fit together.
|
||||
@ -355,7 +355,7 @@ type ComparisonExp b = OpExpG b (UnpreparedValue b)
|
||||
-- instead of other type class methods.
|
||||
--
|
||||
-- When we do this, the function call sites (which will often be in @instance
|
||||
-- BackendSchema ...@) becomes the centralised place where we decide which behavior
|
||||
-- BackendSchema ...@) become the centralised places where we decide which behavior
|
||||
-- variation to follow.
|
||||
--
|
||||
-- When faced with answering the question of "what does this method do, and how does
|
||||
|
@ -2,7 +2,8 @@
|
||||
{-# LANGUAGE TemplateHaskell #-}
|
||||
|
||||
module Hasura.GraphQL.Schema.BoolExp
|
||||
( boolExp,
|
||||
( AggregationPredicatesSchema (..),
|
||||
boolExp,
|
||||
mkBoolOperator,
|
||||
equalityOperators,
|
||||
comparisonOperators,
|
||||
@ -15,7 +16,7 @@ import Data.Text.Casing qualified as C
|
||||
import Data.Text.Extended
|
||||
import Hasura.GraphQL.Parser.Class
|
||||
import Hasura.GraphQL.Schema.Backend
|
||||
import Hasura.GraphQL.Schema.Common (SchemaContext (..), askTableInfo, partialSQLExpToUnpreparedValue, retrieve)
|
||||
import Hasura.GraphQL.Schema.Common (MonadBuildSchemaBase, SchemaContext (..), askTableInfo, partialSQLExpToUnpreparedValue, retrieve)
|
||||
import Hasura.GraphQL.Schema.NamingCase
|
||||
import Hasura.GraphQL.Schema.Options qualified as Options
|
||||
import Hasura.GraphQL.Schema.Parser
|
||||
@ -39,8 +40,35 @@ import Hasura.RQL.Types.SchemaCache hiding (askTableInfo)
|
||||
import Hasura.RQL.Types.Source
|
||||
import Hasura.RQL.Types.SourceCustomization
|
||||
import Hasura.RQL.Types.Table
|
||||
import Hasura.SQL.Backend (BackendType)
|
||||
import Language.GraphQL.Draft.Syntax qualified as G
|
||||
|
||||
-- | Backends implement this type class to specify the schema of
|
||||
-- aggregation predicates.
|
||||
--
|
||||
-- The default implementation results in a parser that does not parse anything.
|
||||
--
|
||||
-- The scope of this class is local to the function 'boolExp'. In particular,
|
||||
-- methods in `class BackendSchema` and `type MonadBuildSchema` should *NOT*
|
||||
-- include this class as a constraint.
|
||||
class AggregationPredicatesSchema (b :: BackendType) where
|
||||
aggregationPredicatesParser ::
|
||||
forall r m n.
|
||||
MonadBuildSchemaBase r m n =>
|
||||
SourceInfo b ->
|
||||
TableInfo b ->
|
||||
m (Maybe (InputFieldsParser n [AggregationPredicates b (UnpreparedValue b)]))
|
||||
|
||||
-- Overlapping instance for backends that do not implement Aggregation Predicates.
|
||||
instance {-# OVERLAPPABLE #-} (AggregationPredicates b ~ Const Void) => AggregationPredicatesSchema (b :: BackendType) where
|
||||
aggregationPredicatesParser ::
|
||||
forall r m n.
|
||||
(MonadBuildSchemaBase r m n) =>
|
||||
SourceInfo b ->
|
||||
TableInfo b ->
|
||||
m (Maybe (InputFieldsParser n [AggregationPredicates b (UnpreparedValue b)]))
|
||||
aggregationPredicatesParser _ _ = return Nothing
|
||||
|
||||
-- |
|
||||
-- > input type_bool_exp {
|
||||
-- > _or: [type_bool_exp!]
|
||||
@ -51,7 +79,7 @@ import Language.GraphQL.Draft.Syntax qualified as G
|
||||
-- > }
|
||||
boolExp ::
|
||||
forall b r m n.
|
||||
MonadBuildSchema b r m n =>
|
||||
(MonadBuildSchema b r m n, AggregationPredicatesSchema b) =>
|
||||
SourceInfo b ->
|
||||
TableInfo b ->
|
||||
m (Parser 'Input n (AnnBoolExp b (UnpreparedValue b)))
|
||||
@ -66,10 +94,12 @@ boolExp sourceInfo tableInfo = P.memoizeOn 'boolExp (_siName sourceInfo, tableNa
|
||||
|
||||
fieldInfos <- tableSelectFields sourceInfo tableInfo
|
||||
tableFieldParsers <- catMaybes <$> traverse mkField fieldInfos
|
||||
-- TODO: This naming is somewhat unsatifactory..
|
||||
aggregationPredicatesParser' <- fromMaybe (pure []) <$> aggregationPredicatesParser sourceInfo tableInfo
|
||||
recur <- boolExp sourceInfo tableInfo
|
||||
-- Bafflingly, ApplicativeDo doesn’t work if we inline this definition (I
|
||||
-- think the TH splices throw it off), so we have to define it separately.
|
||||
let specialFieldParsers =
|
||||
let connectiveFieldParsers =
|
||||
[ P.fieldOptional Name.__or Nothing (BoolOr <$> P.list recur),
|
||||
P.fieldOptional Name.__and Nothing (BoolAnd <$> P.list recur),
|
||||
P.fieldOptional Name.__not Nothing (BoolNot <$> recur)
|
||||
@ -78,8 +108,9 @@ boolExp sourceInfo tableInfo = P.memoizeOn 'boolExp (_siName sourceInfo, tableNa
|
||||
pure $
|
||||
BoolAnd <$> P.object name (Just description) do
|
||||
tableFields <- map BoolField . catMaybes <$> sequenceA tableFieldParsers
|
||||
specialFields <- catMaybes <$> sequenceA specialFieldParsers
|
||||
pure (tableFields ++ specialFields)
|
||||
specialFields <- catMaybes <$> sequenceA connectiveFieldParsers
|
||||
aggregationPredicateFields <- map (BoolField . AVAggregationPredicates) <$> aggregationPredicatesParser'
|
||||
pure (tableFields ++ specialFields ++ aggregationPredicateFields)
|
||||
where
|
||||
tableName = tableInfoName tableInfo
|
||||
|
||||
|
@ -0,0 +1,167 @@
|
||||
{-# LANGUAGE ApplicativeDo #-}
|
||||
|
||||
-- | This module defines the schema aspect of the default implementation of
|
||||
-- aggregation predicates.
|
||||
module Hasura.GraphQL.Schema.BoolExp.AggregationPredicates
|
||||
( defaultAggregationPredicatesParser,
|
||||
|
||||
-- * Data types describing aggregation functions supported by a backend
|
||||
FunctionSignature (..),
|
||||
ArgumentsSignature (..),
|
||||
ArgumentSignature (..),
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Functor.Compose
|
||||
import Data.List.NonEmpty qualified as NE
|
||||
import Hasura.GraphQL.Parser qualified as P
|
||||
import Hasura.GraphQL.Schema.Backend
|
||||
import Hasura.GraphQL.Schema.BoolExp
|
||||
import Hasura.GraphQL.Schema.Common (MonadBuildSchemaBase, askTableInfo, textToName)
|
||||
import Hasura.GraphQL.Schema.Parser
|
||||
( InputFieldsParser,
|
||||
Kind (..),
|
||||
Parser,
|
||||
)
|
||||
import Hasura.GraphQL.Schema.Table
|
||||
import Hasura.Name qualified as Name
|
||||
import Hasura.Prelude
|
||||
import Hasura.RQL.IR.BoolExp.AggregationPredicates
|
||||
import Hasura.RQL.IR.Value
|
||||
import Hasura.RQL.Types.Backend qualified as B
|
||||
import Hasura.RQL.Types.Column
|
||||
import Hasura.RQL.Types.Common (relNameToTxt)
|
||||
import Hasura.RQL.Types.Relationships.Local
|
||||
import Hasura.RQL.Types.SchemaCache hiding (askTableInfo)
|
||||
import Hasura.RQL.Types.Source
|
||||
import Hasura.RQL.Types.Table
|
||||
import Hasura.SQL.Backend (BackendType)
|
||||
import Language.GraphQL.Draft.Syntax qualified as G
|
||||
|
||||
-- | This function is meant to serve as the default schema for Aggregation
|
||||
-- Predicates represented in the IR by the type
|
||||
-- 'Hasura.RQL.IR.BoolExp.AggregationPredicates.AggregationPredicates'.
|
||||
defaultAggregationPredicatesParser ::
|
||||
forall b r m n.
|
||||
( MonadBuildSchemaBase r m n,
|
||||
BackendSchema b,
|
||||
AggregationPredicatesSchema b
|
||||
) =>
|
||||
[FunctionSignature b] ->
|
||||
SourceInfo b ->
|
||||
TableInfo b ->
|
||||
m (Maybe (InputFieldsParser n [AggregationPredicatesImplementation b (UnpreparedValue b)]))
|
||||
defaultAggregationPredicatesParser aggFns si ti = runMaybeT do
|
||||
arrayRelationships <- fails $ return $ nonEmpty $ tableArrayRelationships ti
|
||||
aggregationFunctions <- fails $ return $ nonEmpty aggFns
|
||||
|
||||
buildAnyOptionalFields $
|
||||
arrayRelationships <&> \rel -> do
|
||||
relTable <- askTableInfo si (riRTable rel)
|
||||
relGqlName <- textToName $ relNameToTxt $ riName rel
|
||||
typeGqlName <- (<> Name.__ <> relGqlName) <$> getTableGQLName relTable
|
||||
|
||||
-- We only make a field for aggregations over a relation if at least
|
||||
-- some aggregation predicates are callable.
|
||||
relAggregateField rel relGqlName typeGqlName
|
||||
-- We only return an InputFieldsParser for aggregation predicates,
|
||||
-- if we parse at least one aggregation predicate
|
||||
<$> buildAnyOptionalFields
|
||||
( aggregationFunctions <&> \FunctionSignature {..} -> do
|
||||
aggPredicateField fnGQLName typeGqlName <$> unfuse do
|
||||
aggPredArguments <-
|
||||
-- We only include an aggregation predicate if we are able to
|
||||
-- access columns all its arguments. This might fail due to
|
||||
-- permissions or due to no columns of suitable types
|
||||
-- existing on the table.
|
||||
case fnArguments of
|
||||
ArgumentsStar ->
|
||||
maybe AggregationPredicateArgumentsStar AggregationPredicateArguments . nonEmpty
|
||||
<$> fuse (fieldOptionalDefault Name._arguments Nothing [] . P.list <$> fails (tableSelectColumnsEnum si relTable))
|
||||
Arguments args ->
|
||||
AggregationPredicateArguments
|
||||
<$> fuse
|
||||
( P.field Name._arguments Nothing
|
||||
. P.object (typeGqlName <> Name.__ <> fnGQLName <> Name.__ <> Name._arguments) Nothing
|
||||
<$> buildAllFieldsNE
|
||||
( args `for` \ArgumentSignature {..} ->
|
||||
P.field argName Nothing <$> fails (tableSelectColumnsPredEnum (== (ColumnScalar argType)) relGqlName si relTable)
|
||||
)
|
||||
)
|
||||
|
||||
aggPredDistinct <- fuse $ return $ fieldOptionalDefault Name._distinct Nothing False P.boolean
|
||||
let aggPredFunctionName = fnName
|
||||
aggPredPredicate <- fuse $ P.field Name._predicate Nothing <$> lift (comparisonExps @b (ColumnScalar fnReturnType))
|
||||
aggPredFilter <- fuse $ P.fieldOptional Name._filter Nothing <$> lift (boolExp si relTable)
|
||||
pure $ AggregationPredicate {..}
|
||||
)
|
||||
where
|
||||
-- Input field of the aggregation predicates for one array relation.
|
||||
relAggregateField ::
|
||||
RelInfo b ->
|
||||
G.Name ->
|
||||
G.Name ->
|
||||
(InputFieldsParser n [AggregationPredicate b (UnpreparedValue b)]) ->
|
||||
(InputFieldsParser n (Maybe (AggregationPredicatesImplementation b (UnpreparedValue b))))
|
||||
relAggregateField rel typeGqlName relGqlName =
|
||||
P.fieldOptional (relGqlName <> Name.__ <> Name._aggregate) Nothing
|
||||
. P.object typeGqlName Nothing
|
||||
. fmap (AggregationPredicatesImplementation rel)
|
||||
|
||||
-- Input field for a single aggregation predicate.
|
||||
aggPredicateField ::
|
||||
G.Name ->
|
||||
G.Name ->
|
||||
InputFieldsParser n (AggregationPredicate b (UnpreparedValue b)) ->
|
||||
InputFieldsParser n (Maybe (AggregationPredicate b (UnpreparedValue b)))
|
||||
aggPredicateField fnGQLName typeGqlName =
|
||||
P.fieldOptional fnGQLName Nothing . P.object (typeGqlName <> Name.__ <> fnGQLName) Nothing
|
||||
|
||||
buildAnyOptionalFields :: NonEmpty (MaybeT m (InputFieldsParser n (Maybe c))) -> MaybeT m (InputFieldsParser n [c])
|
||||
buildAnyOptionalFields = fmap collectOptionalFields . collectBranchesNE
|
||||
where
|
||||
-- Collect a non-empty list of optional input field parsers into one input field
|
||||
-- parser parsing a list of the specified values.
|
||||
collectOptionalFields :: NonEmpty (InputFieldsParser n (Maybe a)) -> InputFieldsParser n [a]
|
||||
collectOptionalFields = fmap (catMaybes . NE.toList) . sequenceA
|
||||
|
||||
buildAllFieldsNE :: MaybeT m (NonEmpty (InputFieldsParser n c)) -> MaybeT m (InputFieldsParser n (NonEmpty c))
|
||||
buildAllFieldsNE = fmap sequenceA
|
||||
|
||||
-- Collect all the non-failed branches, failing if all branches failed.
|
||||
collectBranchesNE :: forall f a. Applicative f => NonEmpty (MaybeT f a) -> MaybeT f (NonEmpty a)
|
||||
collectBranchesNE xs = MaybeT $ NE.nonEmpty . catMaybes . NE.toList <$> sequenceA (xs <&> runMaybeT)
|
||||
|
||||
-- Mark a computation as potentially failing.
|
||||
fails :: m (Maybe a) -> MaybeT m a
|
||||
fails = MaybeT
|
||||
|
||||
-- Compose our monad with InputFieldsParser into one fused Applicative that
|
||||
-- acts on the parsed values directly.
|
||||
fuse :: MaybeT m (InputFieldsParser n a) -> Compose (MaybeT m) (InputFieldsParser n) a
|
||||
fuse = Compose
|
||||
|
||||
-- The inverse of 'fuse'.
|
||||
unfuse :: Compose (MaybeT m) (InputFieldsParser n) a -> MaybeT m (InputFieldsParser n a)
|
||||
unfuse = getCompose
|
||||
|
||||
-- Optional input field with a default value when the field is elided or null.
|
||||
fieldOptionalDefault ::
|
||||
forall k a. ('Input P.<: k) => G.Name -> Maybe G.Description -> a -> Parser k n a -> InputFieldsParser n a
|
||||
fieldOptionalDefault n d a p = fromMaybe a <$> P.fieldOptional n d p
|
||||
|
||||
data FunctionSignature (b :: BackendType) = FunctionSignature
|
||||
{ fnName :: Text,
|
||||
fnGQLName :: G.Name,
|
||||
fnArguments :: ArgumentsSignature b,
|
||||
fnReturnType :: B.ScalarType b
|
||||
}
|
||||
|
||||
data ArgumentsSignature (b :: BackendType)
|
||||
= ArgumentsStar
|
||||
| Arguments (NonEmpty (ArgumentSignature b))
|
||||
|
||||
data ArgumentSignature (b :: BackendType) = ArgumentSignature
|
||||
{ argType :: B.ScalarType b,
|
||||
argName :: G.Name
|
||||
}
|
@ -59,6 +59,7 @@ import Data.Text.Casing qualified as C
|
||||
import Data.Text.Extended
|
||||
import Hasura.GraphQL.ApolloFederation
|
||||
import Hasura.GraphQL.Schema.Backend (BackendTableSelectSchema (..), MonadBuildSchema)
|
||||
import Hasura.GraphQL.Schema.BoolExp (AggregationPredicatesSchema)
|
||||
import Hasura.GraphQL.Schema.Common
|
||||
import Hasura.GraphQL.Schema.Mutation
|
||||
import Hasura.GraphQL.Schema.NamingCase
|
||||
@ -104,6 +105,7 @@ setFieldNameCase tCase tInfo crf getFieldName tableName =
|
||||
buildTableQueryAndSubscriptionFields ::
|
||||
forall b r m n.
|
||||
( MonadBuildSchema b r m n,
|
||||
AggregationPredicatesSchema b,
|
||||
BackendTableSelectSchema b
|
||||
) =>
|
||||
MkRootFieldName ->
|
||||
@ -200,6 +202,7 @@ buildTableQueryAndSubscriptionFields mkRootFieldName sourceInfo tableName tableI
|
||||
buildTableStreamingSubscriptionFields ::
|
||||
forall b r m n.
|
||||
( MonadBuildSchema b r m n,
|
||||
AggregationPredicatesSchema b,
|
||||
BackendTableSelectSchema b
|
||||
) =>
|
||||
MkRootFieldName ->
|
||||
@ -276,6 +279,7 @@ buildTableInsertMutationFields backendInsertAction mkRootFieldName scenario sour
|
||||
buildTableUpdateMutationFields ::
|
||||
forall b r m n.
|
||||
( MonadBuildSchema b r m n,
|
||||
AggregationPredicatesSchema b,
|
||||
BackendTableSelectSchema b
|
||||
) =>
|
||||
-- | an action that builds @BackendUpdate@ with the
|
||||
@ -319,6 +323,7 @@ buildTableUpdateMutationFields mkBackendUpdate mkRootFieldName scenario sourceIn
|
||||
buildTableDeleteMutationFields ::
|
||||
forall b r m n.
|
||||
( MonadBuildSchema b r m n,
|
||||
AggregationPredicatesSchema b,
|
||||
BackendTableSelectSchema b
|
||||
) =>
|
||||
MkRootFieldName ->
|
||||
|
@ -353,6 +353,7 @@ mkInsertObject objects tableInfo backendInsert insertPerms updatePerms =
|
||||
deleteFromTable ::
|
||||
forall b r m n.
|
||||
( MonadBuildSchema b r m n,
|
||||
AggregationPredicatesSchema b,
|
||||
BackendTableSelectSchema b
|
||||
) =>
|
||||
Scenario ->
|
||||
|
@ -149,7 +149,8 @@ defaultSelectTable sourceInfo tableInfo fieldName description = runMaybeT do
|
||||
selectTableConnection ::
|
||||
forall b r m n.
|
||||
( MonadBuildSchema b r m n,
|
||||
BackendTableSelectSchema b
|
||||
BackendTableSelectSchema b,
|
||||
AggregationPredicatesSchema b
|
||||
) =>
|
||||
SourceInfo b ->
|
||||
-- | table info
|
||||
@ -368,9 +369,10 @@ cause errors on the client side, for the following reasons:
|
||||
-- > }
|
||||
defaultTableSelectionSet ::
|
||||
forall b r m n.
|
||||
( MonadBuildSchema b r m n,
|
||||
( AggregationPredicatesSchema b,
|
||||
BackendTableSelectSchema b,
|
||||
Eq (AnnBoolExp b (IR.UnpreparedValue b))
|
||||
Eq (AnnBoolExp b (IR.UnpreparedValue b)),
|
||||
MonadBuildSchema b r m n
|
||||
) =>
|
||||
SourceInfo b ->
|
||||
TableInfo b ->
|
||||
@ -582,7 +584,7 @@ tableConnectionSelectionSet sourceInfo tableInfo = runMaybeT do
|
||||
-- > where: table_bool_exp
|
||||
defaultTableArgs ::
|
||||
forall b r m n.
|
||||
MonadBuildSchema b r m n =>
|
||||
(MonadBuildSchema b r m n, AggregationPredicatesSchema b) =>
|
||||
SourceInfo b ->
|
||||
TableInfo b ->
|
||||
m (InputFieldsParser n (SelectArgs b))
|
||||
@ -630,7 +632,9 @@ defaultTableArgs sourceInfo tableInfo = do
|
||||
-- > where: table_bool_exp
|
||||
tableWhereArg ::
|
||||
forall b r m n.
|
||||
MonadBuildSchema b r m n =>
|
||||
( AggregationPredicatesSchema b,
|
||||
MonadBuildSchema b r m n
|
||||
) =>
|
||||
SourceInfo b ->
|
||||
TableInfo b ->
|
||||
m (InputFieldsParser n (Maybe (IR.AnnBoolExp b (IR.UnpreparedValue b))))
|
||||
@ -723,7 +727,7 @@ tableOffsetArg =
|
||||
-- > after: String
|
||||
tableConnectionArgs ::
|
||||
forall b r m n.
|
||||
MonadBuildSchema b r m n =>
|
||||
(MonadBuildSchema b r m n, AggregationPredicatesSchema b) =>
|
||||
PrimaryKeyColumns b ->
|
||||
SourceInfo b ->
|
||||
TableInfo b ->
|
||||
@ -971,9 +975,10 @@ tableAggregationFields sourceInfo tableInfo =
|
||||
-- > field_name(arg_name: arg_type, ...): field_type
|
||||
fieldSelection ::
|
||||
forall b r m n.
|
||||
( MonadBuildSchema b r m n,
|
||||
( AggregationPredicatesSchema b,
|
||||
BackendTableSelectSchema b,
|
||||
Eq (AnnBoolExp b (IR.UnpreparedValue b))
|
||||
Eq (AnnBoolExp b (IR.UnpreparedValue b)),
|
||||
MonadBuildSchema b r m n
|
||||
) =>
|
||||
SourceInfo b ->
|
||||
TableName b ->
|
||||
@ -1124,9 +1129,10 @@ join) satisfies `p(T)`.
|
||||
-- | Field parsers for a table relationship
|
||||
relationshipField ::
|
||||
forall b r m n.
|
||||
( MonadBuildSchema b r m n,
|
||||
( AggregationPredicatesSchema b,
|
||||
BackendTableSelectSchema b,
|
||||
Eq (AnnBoolExp b (IR.UnpreparedValue b))
|
||||
Eq (AnnBoolExp b (IR.UnpreparedValue b)),
|
||||
MonadBuildSchema b r m n
|
||||
) =>
|
||||
SourceInfo b ->
|
||||
TableName b ->
|
||||
|
@ -14,6 +14,7 @@ import Data.Text.Extended ((<>>))
|
||||
import Hasura.Base.Error (QErr)
|
||||
import Hasura.GraphQL.Parser.Class
|
||||
import Hasura.GraphQL.Schema.Backend
|
||||
import Hasura.GraphQL.Schema.BoolExp (AggregationPredicatesSchema)
|
||||
import Hasura.GraphQL.Schema.Common
|
||||
import Hasura.GraphQL.Schema.NamingCase
|
||||
import Hasura.GraphQL.Schema.Options qualified as Options
|
||||
@ -218,7 +219,9 @@ tableStreamCursorArg sourceInfo tableInfo = do
|
||||
-- > table_stream (cursor: [table_stream_cursor_input]!, batch_size: Int!, where: table_bool_exp)
|
||||
tableStreamArguments ::
|
||||
forall b r m n.
|
||||
MonadBuildSchema b r m n =>
|
||||
( AggregationPredicatesSchema b,
|
||||
MonadBuildSchema b r m n
|
||||
) =>
|
||||
SourceInfo b ->
|
||||
TableInfo b ->
|
||||
m (InputFieldsParser n (SelectStreamArgs b))
|
||||
@ -241,6 +244,7 @@ tableStreamArguments sourceInfo tableInfo = do
|
||||
selectStreamTable ::
|
||||
forall b r m n.
|
||||
( MonadBuildSchema b r m n,
|
||||
AggregationPredicatesSchema b,
|
||||
BackendTableSelectSchema b
|
||||
) =>
|
||||
SourceInfo b ->
|
||||
|
@ -2,6 +2,7 @@
|
||||
module Hasura.GraphQL.Schema.Table
|
||||
( getTableGQLName,
|
||||
tableSelectColumnsEnum,
|
||||
tableSelectColumnsPredEnum,
|
||||
tableUpdateColumnsEnum,
|
||||
updateColumnsPlaceholderParser,
|
||||
tableSelectPermissions,
|
||||
@ -16,6 +17,7 @@ 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)
|
||||
@ -83,7 +85,7 @@ getTableIdentifierName tableInfo =
|
||||
-- permissions for.
|
||||
tableSelectColumnsEnum ::
|
||||
forall b r m n.
|
||||
MonadBuildSchema b r m n =>
|
||||
(Backend b, MonadBuildSchemaBase r m n) =>
|
||||
SourceInfo b ->
|
||||
TableInfo b ->
|
||||
m (Maybe (Parser 'Both n (Column b)))
|
||||
@ -108,6 +110,42 @@ tableSelectColumnsEnum sourceInfo tableInfo = do
|
||||
define name =
|
||||
P.Definition name (Just $ G.Description "column name") Nothing [] 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 columnPredicate predName sourceInfo tableInfo = do
|
||||
tableGQLName <- getTableGQLName @b tableInfo
|
||||
columns <- filter (columnPredicate . ciType) <$> tableSelectColumns sourceInfo tableInfo
|
||||
enumName <- mkTypename $ tableGQLName <> Name.__select_column <> Name.__ <> predName
|
||||
let description =
|
||||
Just $
|
||||
G.Description $
|
||||
pack ("select " ++ show predName ++ "columns of table ") <>> tableInfoName tableInfo
|
||||
pure $
|
||||
P.enum enumName description
|
||||
<$> nonEmpty
|
||||
[ ( define $ ciName column,
|
||||
ciColumn column
|
||||
)
|
||||
| column <- columns
|
||||
]
|
||||
where
|
||||
define name =
|
||||
P.Definition name (Just $ G.Description "column name") Nothing [] P.EnumValueInfo
|
||||
|
||||
-- | Table update columns enum
|
||||
--
|
||||
-- Parser for an enum type that matches the columns of the given
|
||||
|
@ -25,7 +25,7 @@ import Data.Text.Extended (toTxt, (<>>))
|
||||
import Hasura.Base.Error (QErr)
|
||||
import Hasura.Base.ToErrorValue
|
||||
import Hasura.GraphQL.Schema.Backend (BackendSchema (..), BackendTableSelectSchema (..), MonadBuildSchema, columnParser)
|
||||
import Hasura.GraphQL.Schema.BoolExp (boolExp)
|
||||
import Hasura.GraphQL.Schema.BoolExp (AggregationPredicatesSchema, boolExp)
|
||||
import Hasura.GraphQL.Schema.Common (Scenario (..), SchemaContext (..), mapField, partialSQLExpToUnpreparedValue, retrieve)
|
||||
import Hasura.GraphQL.Schema.Mutation (mutationSelectionSet, primaryKeysArguments)
|
||||
import Hasura.GraphQL.Schema.NamingCase
|
||||
@ -256,6 +256,7 @@ incOp = UpdateOperator {..}
|
||||
updateTable ::
|
||||
forall b r m n.
|
||||
( MonadBuildSchema b r m n,
|
||||
AggregationPredicatesSchema b,
|
||||
BackendTableSelectSchema b
|
||||
) =>
|
||||
-- | backend-specific data needed to perform an update mutation
|
||||
|
@ -606,3 +606,14 @@ __Entity = [G.name|_Entity|]
|
||||
|
||||
__entities :: G.Name
|
||||
__entities = [G.name|_entities|]
|
||||
|
||||
-- * Aggregation Predicates
|
||||
|
||||
_arguments :: G.Name
|
||||
_arguments = [G.name|arguments|]
|
||||
|
||||
_predicate :: G.Name
|
||||
_predicate = [G.name|predicate|]
|
||||
|
||||
_filter :: G.Name
|
||||
_filter = [G.name|filter|]
|
||||
|
@ -60,6 +60,7 @@ module Hasura.RQL.Types.Table
|
||||
sortCols,
|
||||
tableInfoName,
|
||||
getRolePermInfo,
|
||||
tableArrayRelationships,
|
||||
tcCustomName,
|
||||
tcCustomRootFields,
|
||||
tcComment,
|
||||
@ -929,6 +930,9 @@ tiName = tiCoreInfo . tciName
|
||||
tableInfoName :: TableInfo b -> TableName b
|
||||
tableInfoName = view tiName
|
||||
|
||||
tableArrayRelationships :: TableInfo b -> [RelInfo b]
|
||||
tableArrayRelationships ti = [rel | rel <- getRels . _tciFieldInfoMap . _tiCoreInfo $ ti, riType rel == ArrRel]
|
||||
|
||||
getRolePermInfo :: RoleName -> TableInfo b -> RolePermInfo b
|
||||
getRolePermInfo role tableInfo
|
||||
| role == adminRoleName = _tiAdminRolePermInfo tableInfo
|
||||
|
Loading…
Reference in New Issue
Block a user