Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: insert embedded resources via computed columns #3226

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions src/PostgREST/ApiRequest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import Data.List (lookup)
import Data.Ranged.Ranges (emptyRange, rangeIntersection,
rangeIsEmpty)
import Data.Tree (Tree (..))
import Network.HTTP.Types.Header (RequestHeaders, hCookie)
import Network.HTTP.Types.URI (parseSimpleQuery)
import Network.Wai (Request (..))
Expand All @@ -45,6 +46,7 @@

import PostgREST.ApiRequest.QueryParams (QueryParams (..))
import PostgREST.ApiRequest.Types (ApiRequestError (..),
ColumnItem (..),
RangeError (..))
import PostgREST.Config (AppConfig (..),
OpenAPIMode (..))
Expand All @@ -54,8 +56,7 @@
hasLimitZero,
rangeRequested)
import PostgREST.SchemaCache (SchemaCache (..))
import PostgREST.SchemaCache.Identifiers (FieldName,
QualifiedIdentifier (..),
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..),
Schema)

import qualified PostgREST.ApiRequest.Preferences as Preferences
Expand Down Expand Up @@ -116,7 +117,7 @@
, iPayload :: Maybe Payload -- ^ Data sent by client and used for mutation actions
, iPreferences :: Preferences.Preferences -- ^ Prefer header values
, iQueryParams :: QueryParams.QueryParams
, iColumns :: S.Set FieldName -- ^ parsed colums from &columns parameter and payload
, iColumns :: S.Set (Tree ColumnItem) -- ^ parsed colums from &columns parameter and payload

Check warning on line 120 in src/PostgREST/ApiRequest.hs

View check run for this annotation

Codecov / codecov/patch

src/PostgREST/ApiRequest.hs#L120

Added line #L120 was not covered by tests
, iHeaders :: [(ByteString, ByteString)] -- ^ HTTP request headers
, iCookies :: [(ByteString, ByteString)] -- ^ Request Cookies
, iPath :: ByteString -- ^ Raw request path
Expand Down Expand Up @@ -236,13 +237,13 @@
isInvalidRange = topLevelRange == emptyRange && not (hasLimitZero limitRange)
topLevelRange = fromMaybe allRange $ HM.lookup "limit" ranges -- if no limit is specified, get all the request rows

getPayload :: RequestBody -> MediaType -> QueryParams.QueryParams -> Action -> Either ApiRequestError (Maybe Payload, S.Set FieldName)
getPayload :: RequestBody -> MediaType -> QueryParams.QueryParams -> Action -> Either ApiRequestError (Maybe Payload, S.Set (Tree ColumnItem))
getPayload reqBody contentMediaType QueryParams{qsColumns} action = do
checkedPayload <- if shouldParsePayload then payload else Right Nothing
let cols = case (checkedPayload, columns) of
(Just ProcessedJSON{payKeys}, _) -> payKeys
(Just ProcessedUrlEncoded{payKeys}, _) -> payKeys
(Just RawJSON{}, Just cls) -> cls
(Just ProcessedJSON{payKeys}, _) -> S.map ((`Node` []) . ColumnField) payKeys
(Just ProcessedUrlEncoded{payKeys}, _) -> S.map ((`Node` []) . ColumnField) payKeys
(Just RawJSON{}, Just cls) -> S.fromList cls
_ -> S.empty
return (checkedPayload, cols)
where
Expand Down
39 changes: 32 additions & 7 deletions src/PostgREST/ApiRequest/QueryParams.hs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@
import PostgREST.SchemaCache.Identifiers (FieldName)

import PostgREST.ApiRequest.Types (AggregateFunction (..),
EmbedParam (..), EmbedPath, Field,
Filter (..), FtsOperator (..),
Hint, JoinType (..),
JsonOperand (..),
ColumnItem (..), EmbedParam (..),
EmbedPath, Field, Filter (..),
FtsOperator (..), Hint,
JoinType (..), JsonOperand (..),
JsonOperation (..), JsonPath,
ListVal, LogicOperator (..),
LogicTree (..), OpExpr (..),
Expand All @@ -73,7 +73,7 @@
-- ^ &order parameters for each level
, qsLogic :: [(EmbedPath, LogicTree)]
-- ^ &and and &or parameters used for complex boolean logic
, qsColumns :: Maybe (S.Set FieldName)
, qsColumns :: Maybe [Tree ColumnItem]

Check warning on line 76 in src/PostgREST/ApiRequest/QueryParams.hs

View check run for this annotation

Codecov / codecov/patch

src/PostgREST/ApiRequest/QueryParams.hs#L76

Added line #L76 was not covered by tests
-- ^ &columns parameter and payload
, qsSelect :: [Tree SelectItem]
-- ^ &select parameter used to shape the response
Expand Down Expand Up @@ -260,11 +260,13 @@
-- in the form of "?and=and(.. , ..)" instead of "?and=(.. , ..)"
P.parse pLogicTree ("failed to parse logic tree (" ++ toS v ++ ")") $ toS (op <> v)

pRequestColumns :: Maybe Text -> Either QPError (Maybe (S.Set FieldName))

-- Satisfies the form: /products?columns=name,sup:suppliers!fk(name)
pRequestColumns :: Maybe Text -> Either QPError (Maybe [Tree ColumnItem])
pRequestColumns colStr =
case colStr of
Just str ->
mapError $ Just . S.fromList <$> P.parse pColumns ("failed to parse columns parameter (" <> toS str <> ")") (toS str)
mapError $ Just <$> P.parse pColumnForest ("failed to parse columns parameter (" <> toS str <> ")") (toS str)
_ -> Right Nothing

ws :: Parser Text
Expand Down Expand Up @@ -477,6 +479,13 @@
try (void $ lookAhead (string "("))
return $ SelectRelation name alias hint jType

pRelationColumn :: Parser ColumnItem
pRelationColumn = lexeme $ do
alias <- optionMaybe ( try(pFieldName <* aliasSeparator) )
name <- pFieldName
(hint, _) <- pEmbedParams
try (void $ lookAhead (string "("))
return $ ColumnRelation name alias hint

Check warning on line 488 in src/PostgREST/ApiRequest/QueryParams.hs

View check run for this annotation

Codecov / codecov/patch

src/PostgREST/ApiRequest/QueryParams.hs#L488

Added line #L488 was not covered by tests

-- |
-- Parse regular fields in select
Expand Down Expand Up @@ -845,6 +854,22 @@
pColumns :: Parser [FieldName]
pColumns = pFieldName `sepBy1` lexeme (char ',')

pColumnForest :: Parser [Tree ColumnItem]
pColumnForest = pColumnTree `sepBy1` lexeme (char ',')
where
pColumnTree = Node <$> try pRelationColumn <*> between (char '(') (char ')') pColumnForest <|>
Node <$> pColumnName <*> pure []

pColumnName :: Parser ColumnItem
pColumnName = lexeme $ do
fld <- pFieldName
pEnd
return $ ColumnField fld
where
pEnd = try (void $ lookAhead (string ")")) <|>
try (void $ lookAhead (string ",")) <|>
try eof

pIdentifier :: Parser Text
pIdentifier = T.strip . toS <$> many1 pIdentifierChar

Expand Down
10 changes: 10 additions & 0 deletions src/PostgREST/ApiRequest/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
, QuantOperator(..)
, FtsOperator(..)
, SelectItem(..)
, ColumnItem(..)
) where

import PostgREST.MediaType (MediaType (..))
Expand Down Expand Up @@ -68,6 +69,15 @@
}
deriving (Eq, Show)

data ColumnItem
= ColumnField FieldName
| ColumnRelation
{ colRelation :: FieldName
, colAlias :: Maybe Alias
, colHint :: Maybe Hint

Check warning on line 77 in src/PostgREST/ApiRequest/Types.hs

View check run for this annotation

Codecov / codecov/patch

src/PostgREST/ApiRequest/Types.hs#L75-L77

Added lines #L75 - L77 were not covered by tests
}
deriving (Eq, Show, Ord)

data ApiRequestError
= AggregatesNotAllowed
| AmbiguousRelBetween Text Text [Relationship]
Expand Down
10 changes: 8 additions & 2 deletions src/PostgREST/Plan.hs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@
callReadPlan identifier conf sCache apiRequest@ApiRequest{iPreferences=Preferences{..},..} invMethod = do
let paramKeys = case invMethod of
InvRead _ -> S.fromList $ fst <$> qsParams'
Inv -> iColumns
Inv -> S.map (getColumnAsText . rootLabel) iColumns
proc@Function{..} <- mapLeft ApiRequestError $
findProc identifier paramKeys (preferParameters == Just SingleObject) (dbRoutines sCache) iContentMediaType (invMethod == Inv)
let relIdentifier = QualifiedIdentifier pdSchema (fromMaybe pdName $ Routine.funcTableName proc) -- done so a set returning function can embed other relations
Expand Down Expand Up @@ -942,7 +942,7 @@
combinedLogic = foldr (addFilterToLogicForest . resolveFilter ctx) logic qsFiltersRoot
body = payRaw <$> iPayload -- the body is assumed to be json at this stage(ApiRequest validates)
applyDefaults = preferMissing == Just ApplyDefaults
typedColumnsOrError = resolveOrError ctx tbl `traverse` S.toList iColumns
typedColumnsOrError = resolveOrError ctx tbl `traverse` S.toList (S.map (getColumnAsText . rootLabel) iColumns)

resolveOrError :: ResolverContext -> Maybe Table -> FieldName -> Either ApiRequestError CoercibleField
resolveOrError _ Nothing _ = Left NotFound
Expand Down Expand Up @@ -1051,3 +1051,9 @@
when' :: Bool -> Maybe a -> Maybe a
when' True (Just a) = Just a
when' _ _ = Nothing

getColumnAsText :: ColumnItem -> Text
getColumnAsText colItem =
case colItem of
ColumnField col -> col
ColumnRelation{colRelation} -> colRelation

Check warning on line 1059 in src/PostgREST/Plan.hs

View check run for this annotation

Codecov / codecov/patch

src/PostgREST/Plan.hs#L1059

Added line #L1059 was not covered by tests
Loading