2022-03-16 07:12:15 +03:00
{- # LANGUAGE DeriveAnyClass # -}
2022-03-31 07:45:03 +03:00
{- # LANGUAGE OverloadedLists # -}
2022-03-16 07:12:15 +03:00
2022-05-02 08:03:12 +03:00
module Hasura.Backends.DataConnector.API.V0.Expression
2022-03-16 07:12:15 +03:00
( Expression ( .. ) ,
2022-09-20 06:59:47 +03:00
ExistsInTable ( .. ) ,
2022-06-02 05:06:45 +03:00
BinaryComparisonOperator ( .. ) ,
BinaryArrayComparisonOperator ( .. ) ,
UnaryComparisonOperator ( .. ) ,
2022-06-24 09:58:25 +03:00
ComparisonColumn ( .. ) ,
2022-09-20 06:59:47 +03:00
ColumnPath ( .. ) ,
2022-06-02 05:06:45 +03:00
ComparisonValue ( .. ) ,
2022-03-16 07:12:15 +03:00
)
where
2022-03-31 07:45:03 +03:00
import Autodocodec.Extended
import Autodocodec.OpenAPI ( )
2022-04-01 04:20:23 +03:00
import Control.DeepSeq ( NFData )
2022-09-06 07:24:46 +03:00
import Data.Aeson ( FromJSON , ToJSON , Value )
2022-04-01 04:20:23 +03:00
import Data.Data ( Data )
2022-07-27 08:27:34 +03:00
import Data.HashMap.Strict qualified as HashMap
2022-04-01 04:20:23 +03:00
import Data.Hashable ( Hashable )
2022-03-31 07:45:03 +03:00
import Data.OpenApi ( ToSchema )
2022-07-15 06:27:31 +03:00
import Data.Text ( Text )
2022-07-27 08:27:34 +03:00
import Data.Tuple.Extra
2022-04-01 04:20:23 +03:00
import GHC.Generics ( Generic )
2022-05-02 08:03:12 +03:00
import Hasura.Backends.DataConnector.API.V0.Column qualified as API . V0
2022-06-24 09:58:25 +03:00
import Hasura.Backends.DataConnector.API.V0.Relationships qualified as API . V0
2022-09-20 06:59:47 +03:00
import Hasura.Backends.DataConnector.API.V0.Table qualified as API . V0
2022-04-01 04:20:23 +03:00
import Prelude
2022-03-16 07:12:15 +03:00
--------------------------------------------------------------------------------
2022-06-02 05:06:45 +03:00
-- | A serializable representation of binary comparison operators.
data BinaryComparisonOperator
2022-03-16 07:12:15 +03:00
= LessThan
| LessThanOrEqual
| GreaterThan
| GreaterThanOrEqual
2022-04-28 04:51:58 +03:00
| Equal
2022-07-15 06:27:31 +03:00
| CustomBinaryComparisonOperator { getCustomBinaryComparisonOperator :: Text }
deriving stock ( Data , Eq , Generic , Ord , Show )
2022-04-01 04:20:23 +03:00
deriving anyclass ( Hashable , NFData )
2022-06-02 05:06:45 +03:00
deriving ( FromJSON , ToJSON , ToSchema ) via Autodocodec BinaryComparisonOperator
2022-03-31 07:45:03 +03:00
2022-06-02 05:06:45 +03:00
instance HasCodec BinaryComparisonOperator where
2022-03-31 07:45:03 +03:00
codec =
2022-06-02 05:06:45 +03:00
named " BinaryComparisonOperator " $
2022-07-15 06:27:31 +03:00
matchChoiceCodec
2022-07-27 08:27:34 +03:00
( stringConstCodec
2022-07-15 06:27:31 +03:00
[ ( LessThan , " less_than " ) ,
( LessThanOrEqual , " less_than_or_equal " ) ,
( GreaterThan , " greater_than " ) ,
( GreaterThanOrEqual , " greater_than_or_equal " ) ,
( Equal , " equal " )
]
)
( dimapCodec CustomBinaryComparisonOperator getCustomBinaryComparisonOperator textCodec )
2022-09-06 07:24:46 +03:00
\ case
2022-07-15 06:27:31 +03:00
op @ CustomBinaryComparisonOperator { } -> Right op
op -> Left op
2022-03-31 07:45:03 +03:00
2022-06-02 05:06:45 +03:00
-- | A serializable representation of binary array comparison operators.
data BinaryArrayComparisonOperator
= In
2022-07-15 06:27:31 +03:00
| CustomBinaryArrayComparisonOperator { getCustomBinaryArrayComparisonOperator :: Text }
deriving stock ( Data , Eq , Generic , Ord , Show )
2022-06-02 05:06:45 +03:00
deriving anyclass ( Hashable , NFData )
deriving ( FromJSON , ToJSON , ToSchema ) via Autodocodec BinaryArrayComparisonOperator
instance HasCodec BinaryArrayComparisonOperator where
codec =
named " BinaryArrayComparisonOperator " $
2022-07-15 06:27:31 +03:00
matchChoiceCodec
2022-07-27 08:27:34 +03:00
( stringConstCodec
2022-07-15 06:27:31 +03:00
[ ( In , " in " )
]
)
( dimapCodec CustomBinaryArrayComparisonOperator getCustomBinaryArrayComparisonOperator textCodec )
2022-09-06 07:24:46 +03:00
\ case
2022-07-15 06:27:31 +03:00
op @ CustomBinaryArrayComparisonOperator { } -> Right op
op -> Left op
2022-06-02 05:06:45 +03:00
-- | A serializable representation of unary comparison operators.
data UnaryComparisonOperator
= IsNull
2022-07-15 06:27:31 +03:00
| CustomUnaryComparisonOperator { getCustomUnaryComparisonOperator :: Text }
deriving stock ( Data , Eq , Generic , Ord , Show )
2022-06-02 05:06:45 +03:00
deriving anyclass ( Hashable , NFData )
deriving ( FromJSON , ToJSON , ToSchema ) via Autodocodec UnaryComparisonOperator
instance HasCodec UnaryComparisonOperator where
codec =
named " UnaryComparisonOperator " $
2022-07-15 06:27:31 +03:00
matchChoiceCodec
2022-07-27 08:27:34 +03:00
( stringConstCodec
2022-07-15 06:27:31 +03:00
[ ( IsNull , " is_null " )
]
)
( dimapCodec CustomUnaryComparisonOperator getCustomUnaryComparisonOperator textCodec )
2022-09-06 07:24:46 +03:00
\ case
2022-07-15 06:27:31 +03:00
op @ CustomUnaryComparisonOperator { } -> Right op
op -> Left op
2022-03-31 07:45:03 +03:00
2022-09-20 09:18:46 +03:00
-- | A serializable representation of query filter expressions.
2022-03-31 07:45:03 +03:00
data Expression
2022-09-20 09:18:46 +03:00
= -- | A logical AND fold
And [ Expression ]
| -- | A logical OR fold
Or [ Expression ]
| -- | A logical NOT function
Not Expression
| -- | There must exist a row in the table specified by 'ExistsInTable' that
-- satisfies the 'Expression'
Exists ExistsInTable Expression
| -- | Apply a 'BinaryComparisonOperator' that compares a column to a 'ComparisonValue';
-- the result of this application will return "true" or "false" depending on the
-- 'BinaryComparisonOperator' that's being applied.
ApplyBinaryComparisonOperator BinaryComparisonOperator ComparisonColumn ComparisonValue
| -- | Apply a 'BinaryArrayComparisonOperator' that evaluates a column with the
-- 'BinaryArrayComparisonOperator' against an array of 'ComparisonValue's.
-- The result of this application will return "true" or "false" depending
-- on the 'BinaryArrayComparisonOperator' that's being applied.
ApplyBinaryArrayComparisonOperator BinaryArrayComparisonOperator ComparisonColumn [ Value ]
| -- | Apply a 'UnaryComparisonOperator' that evaluates a column with the
-- 'UnaryComparisonOperator'; the result of this application will return "true" or
-- "false" depending on the 'UnaryComparisonOperator' that's being applied.
ApplyUnaryComparisonOperator UnaryComparisonOperator ComparisonColumn
2022-06-02 05:06:45 +03:00
deriving stock ( Data , Eq , Generic , Ord , Show )
deriving anyclass ( Hashable , NFData )
2022-07-27 08:27:34 +03:00
deriving ( FromJSON , ToJSON , ToSchema ) via Autodocodec Expression
instance HasCodec Expression where
codec =
named " Expression " $
object " Expression " $
discriminatedUnionCodec " type " enc dec
where
expressionsCodec = requiredField' " expressions "
expressionCodec = requiredField' " expression "
2022-09-20 06:59:47 +03:00
existsCodec =
( , )
<$> requiredField' " in_table " .= fst
<*> requiredField' " where " .= snd
2022-07-27 08:27:34 +03:00
binaryOperatorCodec =
( , , )
<$> requiredField' " operator " .= fst3
<*> requiredField' " column " .= snd3
<*> requiredField' " value " .= thd3
binaryArrayOperatorCodec =
( , , )
<$> requiredField' " operator " .= fst3
<*> requiredField' " column " .= snd3
<*> requiredField' " values " .= thd3
unaryOperatorCodec =
( , )
<$> requiredField' " operator " .= fst
<*> requiredField' " column " .= snd
enc = \ case
And expressions -> ( " and " , mapToEncoder expressions expressionsCodec )
Or expressions -> ( " or " , mapToEncoder expressions expressionsCodec )
Not expression -> ( " not " , mapToEncoder expression expressionCodec )
2022-09-20 06:59:47 +03:00
Exists inTable where' ->
( " exists " , mapToEncoder ( inTable , where' ) existsCodec )
2022-07-27 08:27:34 +03:00
ApplyBinaryComparisonOperator o c v ->
( " binary_op " , mapToEncoder ( o , c , v ) binaryOperatorCodec )
ApplyBinaryArrayComparisonOperator o c vs ->
( " binary_arr_op " , mapToEncoder ( o , c , vs ) binaryArrayOperatorCodec )
ApplyUnaryComparisonOperator o c ->
( " unary_op " , mapToEncoder ( o , c ) unaryOperatorCodec )
dec =
HashMap . fromList
[ ( " and " , ( " AndExpression " , mapToDecoder And expressionsCodec ) ) ,
( " or " , ( " OrExpression " , mapToDecoder Or expressionsCodec ) ) ,
( " not " , ( " NotExpression " , mapToDecoder Not expressionCodec ) ) ,
2022-09-20 06:59:47 +03:00
( " exists " ,
( " ExistsExpression " ,
mapToDecoder ( uncurry Exists ) existsCodec
)
) ,
2022-07-27 08:27:34 +03:00
( " binary_op " ,
( " ApplyBinaryComparisonOperator " ,
mapToDecoder ( uncurry3 ApplyBinaryComparisonOperator ) binaryOperatorCodec
)
) ,
( " binary_arr_op " ,
( " ApplyBinaryArrayComparisonOperator " ,
mapToDecoder ( uncurry3 ApplyBinaryArrayComparisonOperator ) binaryArrayOperatorCodec
)
) ,
( " unary_op " ,
( " ApplyUnaryComparisonOperator " ,
mapToDecoder ( uncurry ApplyUnaryComparisonOperator ) unaryOperatorCodec
)
)
]
2022-06-02 05:06:45 +03:00
2022-09-20 06:59:47 +03:00
-- | Which table should be subqueried to satisfy the 'Exists' expression
data ExistsInTable
= -- | The table is the one found by navigating the specified relationship
-- from the current table
RelatedTable API . V0 . RelationshipName
| -- | The table is completely unrelated to the current table (ie no join
-- between the current table and the specified table should be performed
-- and the whole of the specified table would be subqueried)
UnrelatedTable API . V0 . TableName
deriving stock ( Data , Eq , Generic , Ord , Show )
deriving anyclass ( Hashable , NFData )
deriving ( FromJSON , ToJSON , ToSchema ) via Autodocodec ExistsInTable
instance HasCodec ExistsInTable where
codec = named " ExistsInTable " . object " ExistsInTable " $ discriminatedUnionCodec " type " enc dec
where
relatedTableCodec = requiredField' " relationship "
unrelatedTableCodec = requiredField' " table "
enc = \ case
RelatedTable relationship -> ( " related " , mapToEncoder relationship relatedTableCodec )
UnrelatedTable tableName -> ( " unrelated " , mapToEncoder tableName unrelatedTableCodec )
dec =
HashMap . fromList
[ ( " related " , ( " RelatedTable " , mapToDecoder RelatedTable relatedTableCodec ) ) ,
( " unrelated " , ( " UnrelatedTable " , mapToDecoder UnrelatedTable unrelatedTableCodec ) )
]
2022-06-24 09:58:25 +03:00
-- | Specifies a particular column to use in a comparison via its path and name
data ComparisonColumn = ComparisonColumn
2022-09-20 06:59:47 +03:00
{ -- | The path to the table that contains the specified column.
_ccPath :: ColumnPath ,
2022-06-24 09:58:25 +03:00
-- | The name of the column
_ccName :: API . V0 . ColumnName
}
deriving stock ( Eq , Ord , Show , Generic , Data )
deriving ( FromJSON , ToJSON , ToSchema ) via Autodocodec ComparisonColumn
deriving anyclass ( Hashable , NFData )
instance HasCodec ComparisonColumn where
codec =
object " ComparisonColumn " $
ComparisonColumn
2022-09-20 06:59:47 +03:00
<$> optionalFieldWithOmittedDefault " path " CurrentTable " The path to the table that contains the specified column. Missing or empty array means the current table. [ \ " $ \ " ] means the query table. No other values are supported at this time. " .= _ccPath
2022-06-24 09:58:25 +03:00
<*> requiredField " name " " The name of the column " .= _ccName
2022-09-20 06:59:47 +03:00
-- | Describes what table a column is located on. This may either be the "current" table
-- (which would be query table, or the table specified by the closest ancestor 'Exists'
-- expression), or the query table (meaning the table being queried by the 'Query' which
-- the current 'Expression' is from)
--
-- This currently encodes to @[]@ or @["$"]@ in JSON. This format has been chosen to ensure
-- that if we want to extend the pathing to allow navigation of table relationships by
-- turning this type into a list of path components, we can do that without breaking the
-- JSON format. The JSON format also aligns with how HGE encodes this concept in @_ceq@ etc
-- operators in the permissions system.
data ColumnPath
= CurrentTable
| QueryTable
deriving stock ( Eq , Ord , Show , Generic , Data )
deriving ( FromJSON , ToJSON , ToSchema ) via Autodocodec ColumnPath
deriving anyclass ( Hashable , NFData )
instance HasCodec ColumnPath where
codec = bimapCodec decode encode codec
where
decode :: [ Text ] -> Either String ColumnPath
decode = \ case
[] -> Right CurrentTable
[ " $ " ] -> Right QueryTable
_otherwise -> Left " Invalid ColumnPath "
encode :: ColumnPath -> [ Text ]
encode = \ case
CurrentTable -> []
QueryTable -> [ " $ " ]
2022-06-02 05:06:45 +03:00
-- | A serializable representation of comparison values used in comparisons inside 'Expression's.
data ComparisonValue
2022-06-24 09:58:25 +03:00
= -- | Allows a comparison to a column on the current table or another table
2022-07-27 08:27:34 +03:00
AnotherColumn ComparisonColumn
2022-09-06 07:24:46 +03:00
| ScalarValue Value
2022-03-16 07:12:15 +03:00
deriving stock ( Data , Eq , Generic , Ord , Show )
2022-04-01 04:20:23 +03:00
deriving anyclass ( Hashable , NFData )
2022-07-27 08:27:34 +03:00
deriving ( FromJSON , ToJSON , ToSchema ) via Autodocodec ComparisonValue
2022-06-02 05:06:45 +03:00
instance HasCodec ComparisonValue where
codec =
2022-07-27 08:27:34 +03:00
object " ComparisonValue " $
discriminatedUnionCodec " type " enc dec
where
columnCodec = requiredField' " column "
scalarValueCodec = requiredField' " value "
enc = \ case
AnotherColumn c -> ( " column " , mapToEncoder c columnCodec )
ScalarValue v -> ( " scalar " , mapToEncoder v scalarValueCodec )
dec =
HashMap . fromList
[ ( " column " , ( " AnotherColumnComparison " , mapToDecoder AnotherColumn columnCodec ) ) ,
( " scalar " , ( " ScalarValueComparison " , mapToDecoder ScalarValue scalarValueCodec ) )
]