Skip to content

Commit

Permalink
change Prefer header name
Browse files Browse the repository at this point in the history
  • Loading branch information
steve-chavez committed Mar 21, 2023
1 parent b7c4518 commit 955d3c6
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 37 deletions.
27 changes: 15 additions & 12 deletions src/PostgREST/ApiRequest/Preferences.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
module PostgREST.ApiRequest.Preferences
( Preferences(..)
, PreferCount(..)
, PreferDefaults(..)
, PreferUndefinedKeys(..)
, PreferParameters(..)
, PreferRepresentation(..)
, PreferResolution(..)
Expand All @@ -34,6 +34,7 @@ import Protolude
-- >>> deriving instance Show PreferParameters
-- >>> deriving instance Show PreferCount
-- >>> deriving instance Show PreferTransaction
-- >>> deriving instance Show PreferUndefinedKeys
-- >>> deriving instance Show Preferences

-- | Preferences recognized by the application.
Expand All @@ -44,7 +45,7 @@ data Preferences
, preferParameters :: Maybe PreferParameters
, preferCount :: Maybe PreferCount
, preferTransaction :: Maybe PreferTransaction
, preferDefaults :: Maybe PreferDefaults
, preferUndefinedKeys :: Maybe PreferUndefinedKeys
}

-- |
Expand All @@ -59,6 +60,7 @@ data Preferences
-- , preferParameters = Nothing
-- , preferCount = Just ExactCount
-- , preferTransaction = Nothing
-- , preferUndefinedKeys = Nothing
-- }
--
-- Multiple headers can also be used:
Expand All @@ -70,6 +72,7 @@ data Preferences
-- , preferParameters = Nothing
-- , preferCount = Just ExactCount
-- , preferTransaction = Nothing
-- , preferUndefinedKeys = Nothing
-- }
--
-- If a preference is set more than once, only the first is used:
Expand All @@ -94,14 +97,14 @@ data Preferences
--
-- Preferences can be separated by arbitrary amounts of space, lower-case header is also recognized:
--
-- >>> pPrint $ fromHeaders [("prefer", "count=exact, tx=commit ,return=representation , defaults=apply")]
-- >>> pPrint $ fromHeaders [("prefer", "count=exact, tx=commit ,return=representation , undefined-keys=apply-defaults")]
-- Preferences
-- { preferResolution = Nothing
-- , preferRepresentation = Full
-- , preferParameters = Nothing
-- , preferCount = Just ExactCount
-- , preferTransaction = Just Commit
-- , preferDefaults = Just ApplyDefaults
-- , preferUndefinedKeys = Just ApplyDefaults
-- }
--
fromHeaders :: [HTTP.Header] -> Preferences
Expand All @@ -112,7 +115,7 @@ fromHeaders headers =
, preferParameters = parsePrefs [SingleObject, MultipleObjects]
, preferCount = parsePrefs [ExactCount, PlannedCount, EstimatedCount]
, preferTransaction = parsePrefs [Commit, Rollback]
, preferDefaults = parsePrefs [ApplyDefaults, IgnoreDefaults]
, preferUndefinedKeys = parsePrefs [ApplyDefaults, IgnoreDefaults]
}
where
prefHeaders = filter ((==) HTTP.hPrefer . fst) headers
Expand Down Expand Up @@ -211,14 +214,14 @@ instance ToAppliedHeader PreferTransaction

-- |
-- How to handle the insertion/update when the keys specified in ?columns are not present
-- in the json body.
data PreferDefaults
-- in the json body.
data PreferUndefinedKeys
= ApplyDefaults -- ^ Use the default column value for the unspecified keys.
| IgnoreDefaults -- ^ Inserts: null values / Updates: the keys are not SET to any value
| IgnoreDefaults -- ^ Inserts: null values / Updates: the keys are not SET to any value
deriving Eq

instance ToHeaderValue PreferDefaults where
toHeaderValue ApplyDefaults = "defaults=apply"
toHeaderValue IgnoreDefaults = "defaults=ignore"
instance ToHeaderValue PreferUndefinedKeys where
toHeaderValue ApplyDefaults = "undefined-keys=apply-defaults"
toHeaderValue IgnoreDefaults = "undefined-keys=ignore-defaults"

instance ToAppliedHeader PreferDefaults
instance ToAppliedHeader PreferUndefinedKeys
4 changes: 2 additions & 2 deletions src/PostgREST/Plan.hs
Original file line number Diff line number Diff line change
Expand Up @@ -504,9 +504,9 @@ mutatePlan :: Mutation -> QualifiedIdentifier -> ApiRequest -> SchemaCache -> Re
mutatePlan mutation qi ApiRequest{iPreferences=Preferences{..}, ..} sCache readReq = mapLeft ApiRequestError $
case mutation of
MutationCreate ->
mapRight (\typedColumns -> Insert qi typedColumns body ((,) <$> preferResolution <*> Just confCols) [] returnings pkCols $ preferDefaults == Just ApplyDefaults) typedColumnsOrError
mapRight (\typedColumns -> Insert qi typedColumns body ((,) <$> preferResolution <*> Just confCols) [] returnings pkCols $ preferUndefinedKeys == Just ApplyDefaults) typedColumnsOrError
MutationUpdate ->
mapRight (\typedColumns -> Update qi typedColumns body combinedLogic iTopLevelRange rootOrder returnings $ preferDefaults == Just ApplyDefaults) typedColumnsOrError
mapRight (\typedColumns -> Update qi typedColumns body combinedLogic iTopLevelRange rootOrder returnings $ preferUndefinedKeys == Just ApplyDefaults) typedColumnsOrError
MutationSingleUpsert ->
if null qsLogic &&
qsFilterFields == S.fromList pkCols &&
Expand Down
21 changes: 10 additions & 11 deletions src/PostgREST/Response.hs
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,7 @@ createResponse QualifiedIdentifier{..} MutateReadPlan{mrMutatePlan} ctxApiReques
Nothing
else
toAppliedHeader <$> preferResolution
, if null iColumns || isNothing preferDefaults then
Nothing
else
toAppliedHeader <$> preferDefaults
, undefinedKeysHeader ctxApiRequest
]

if preferRepresentation == Full then
Expand All @@ -124,19 +121,14 @@ createResponse QualifiedIdentifier{..} MutateReadPlan{mrMutatePlan} ctxApiReques
Wai.responseLBS HTTP.status200 (contentTypeHeaders ctxApiRequest) $ LBS.fromStrict plan

updateResponse :: ApiRequest -> ResultSet -> Wai.Response
updateResponse ctxApiRequest@ApiRequest{iPreferences=Preferences{..}, iColumns} resultSet = case resultSet of
updateResponse ctxApiRequest@ApiRequest{iPreferences=Preferences{..}} resultSet = case resultSet of
RSStandard{..} -> do
let
response = gucResponse rsGucStatus rsGucHeaders
contentRangeHeader =
Just . RangeQuery.contentRangeH 0 (rsQueryTotal - 1) $
if shouldCount preferCount then Just rsQueryTotal else Nothing
preferDefaultsHeader =
if null iColumns || isNothing preferDefaults then
Nothing
else
toAppliedHeader <$> preferDefaults
headers = catMaybes [contentRangeHeader, preferDefaultsHeader]
headers = catMaybes [contentRangeHeader, undefinedKeysHeader ctxApiRequest]

if preferRepresentation == Full then
response HTTP.status200
Expand Down Expand Up @@ -271,6 +263,13 @@ profileHeader schema negotiatedByProfile =
else
Nothing

undefinedKeysHeader :: ApiRequest -> Maybe HTTP.Header
undefinedKeysHeader ApiRequest{iPreferences=Preferences{preferUndefinedKeys}, iColumns} =
if null iColumns || isNothing preferUndefinedKeys then
Nothing
else
toAppliedHeader <$> preferUndefinedKeys

addRetryHint :: Int -> Wai.Response -> Wai.Response
addRetryHint delay response = do
let h = ("Retry-After", BS.pack $ show delay)
Expand Down
8 changes: 4 additions & 4 deletions test/spec/Feature/Query/InsertSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ spec actualPgVersion = do
-- inserting the array fails on pg 9.6, but the feature should work normally
when (actualPgVersion >= pgVersion100) $
it "inserts table default values(field-with_sep) when json keys are undefined" $
request methodPost "/complex_items?columns=id,name,field-with_sep,arr_data" [("Prefer", "return=representation"), ("Prefer", "defaults=apply")]
request methodPost "/complex_items?columns=id,name,field-with_sep,arr_data" [("Prefer", "return=representation"), ("Prefer", "undefined-keys=apply-defaults")]
[json|[
{"id": 4, "name": "Vier"},
{"id": 5, "name": "Funf", "arr_data": null},
Expand All @@ -468,11 +468,11 @@ spec actualPgVersion = do
{"id": 6, "name": "Sechs", "field-with_sep": 6, "settings":null,"arr_data":[1,2,3]}
]|]
{ matchStatus = 201
, matchHeaders = ["Preference-Applied" <:> "defaults=apply"]
, matchHeaders = ["Preference-Applied" <:> "undefined-keys=apply-defaults"]
}

it "inserts view default values(field-with_sep) when json keys are undefined" $
request methodPost "/complex_items_view?columns=id,name" [("Prefer", "return=representation"), ("Prefer", "defaults=apply")]
request methodPost "/complex_items_view?columns=id,name" [("Prefer", "return=representation"), ("Prefer", "undefined-keys=apply-defaults")]
[json|[
{"id": 7, "name": "Sieben"},
{"id": 8}
Expand All @@ -483,7 +483,7 @@ spec actualPgVersion = do
{"id": 8, "name": "Default", "field-with_sep": 1, "settings":null,"arr_data":null}
]|]
{ matchStatus = 201
, matchHeaders = ["Preference-Applied" <:> "defaults=apply"]
, matchHeaders = ["Preference-Applied" <:> "undefined-keys=apply-defaults"]
}

context "with unicode values" $ do
Expand Down
16 changes: 8 additions & 8 deletions test/spec/Feature/Query/UpdateSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -333,43 +333,43 @@ spec = do
context "apply defaults on undefined keys" $ do
it "updates table using default values(field-with_sep) when json keys are undefined" $ do
request methodPatch "/complex_items?id=eq.3&columns=name,field-with_sep"
[("Prefer", "return=representation"), ("Prefer", "defaults=apply")]
[("Prefer", "return=representation"), ("Prefer", "undefined-keys=apply-defaults")]
[json|{"name": "Tres"}|]
`shouldRespondWith`
[json|[
{"id":3,"name":"Tres","settings":{"foo":{"int":1,"bar":"baz"}},"arr_data":[1,2,3],"field-with_sep":1}
]|]
{ matchStatus = 200
, matchHeaders = ["Preference-Applied" <:> "defaults=apply"]
, matchHeaders = ["Preference-Applied" <:> "undefined-keys=apply-defaults"]
}

it "updates with limit/offset using table default values(field-with_sep) when json keys are undefined" $ do
request methodPatch "/complex_items?select=id,name&columns=name,field-with_sep&limit=1&offset=2&order=id"
[("Prefer", "return=representation"), ("Prefer", "defaults=apply")]
[("Prefer", "return=representation"), ("Prefer", "undefined-keys=apply-defaults")]
[json|{"name": "Tres"}|]
`shouldRespondWith`
[json|[
{"id":3,"name":"Tres"}
]|]
{ matchStatus = 200
, matchHeaders = ["Preference-Applied" <:> "defaults=apply"]
, matchHeaders = ["Preference-Applied" <:> "undefined-keys=apply-defaults"]
}

it "updates table default values(field-with_sep) when json keys are undefined" $ do
request methodPatch "/complex_items?id=eq.3&columns=name,field-with_sep"
[("Prefer", "return=representation"), ("Prefer", "defaults=apply")]
[("Prefer", "return=representation"), ("Prefer", "undefined-keys=apply-defaults")]
[json|{"name": "Tres"}|]
`shouldRespondWith`
[json|[
{"id":3,"name":"Tres","settings":{"foo":{"int":1,"bar":"baz"}},"arr_data":[1,2,3],"field-with_sep":1}
]|]
{ matchStatus = 200
, matchHeaders = ["Preference-Applied" <:> "defaults=apply"]
, matchHeaders = ["Preference-Applied" <:> "undefined-keys=apply-defaults"]
}

it "updates view default values(field-with_sep) when json keys are undefined" $
request methodPatch "/complex_items_view?id=eq.3&columns=arr_data,name"
[("Prefer", "return=representation"), ("Prefer", "defaults=apply")]
[("Prefer", "return=representation"), ("Prefer", "undefined-keys=apply-defaults")]
[json|
{"arr_data":null}
|]
Expand All @@ -378,7 +378,7 @@ spec = do
{"id":3,"name":"Default","settings":{"foo":{"int":1,"bar":"baz"}},"arr_data":null,"field-with_sep":3}
]|]
{ matchStatus = 200
, matchHeaders = ["Preference-Applied" <:> "defaults=apply"]
, matchHeaders = ["Preference-Applied" <:> "undefined-keys=apply-defaults"]
}

context "tables with self reference foreign keys" $ do
Expand Down

0 comments on commit 955d3c6

Please sign in to comment.