feat: make db-root-spec stable (#2694)
This commit is contained in:
@@ -22,6 +22,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- #1100, Customizable OpenAPI title - @AnthonyFisi
|
||||
- #2506, Add `server-trace-header` for tracing HTTP requests. - @steve-chavez
|
||||
+ When the client sends the request header specified in the config it will be included in the response headers.
|
||||
- #2694, Make `db-root-spec` stable. - @steve-chavez
|
||||
+ This can be used to override the OpenAPI spec with a custom database function
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -111,12 +111,11 @@ postgrest conf appState connWorker =
|
||||
appConf <- AppState.getConfig appState -- the config must be read again because it can reload
|
||||
maybeSchemaCache <- AppState.getSchemaCache appState
|
||||
pgVer <- AppState.getPgVersion appState
|
||||
jsonDbS <- AppState.getJsonDbS appState
|
||||
|
||||
let
|
||||
eitherResponse :: IO (Either Error Wai.Response)
|
||||
eitherResponse =
|
||||
runExceptT $ postgrestResponse appState appConf maybeSchemaCache jsonDbS pgVer authResult req
|
||||
runExceptT $ postgrestResponse appState appConf maybeSchemaCache pgVer authResult req
|
||||
|
||||
response <- either Error.errorResponseFor identity <$> eitherResponse
|
||||
-- Launch the connWorker when the connection is down. The postgrest
|
||||
@@ -132,12 +131,11 @@ postgrestResponse
|
||||
:: AppState.AppState
|
||||
-> AppConfig
|
||||
-> Maybe SchemaCache
|
||||
-> ByteString
|
||||
-> PgVersion
|
||||
-> AuthResult
|
||||
-> Wai.Request
|
||||
-> Handler IO Wai.Response
|
||||
postgrestResponse appState conf@AppConfig{..} maybeSchemaCache jsonDbS pgVer authResult@AuthResult{..} req = do
|
||||
postgrestResponse appState conf@AppConfig{..} maybeSchemaCache pgVer authResult@AuthResult{..} req = do
|
||||
sCache <-
|
||||
case maybeSchemaCache of
|
||||
Just sCache ->
|
||||
@@ -152,7 +150,7 @@ postgrestResponse appState conf@AppConfig{..} maybeSchemaCache jsonDbS pgVer aut
|
||||
ApiRequest.userApiRequest conf sCache req body
|
||||
|
||||
Response.optionalRollback conf apiRequest $
|
||||
handleRequest authResult conf appState (Just authRole /= configDbAnonRole) configDbPreparedStatements jsonDbS pgVer apiRequest sCache
|
||||
handleRequest authResult conf appState (Just authRole /= configDbAnonRole) configDbPreparedStatements pgVer apiRequest sCache
|
||||
|
||||
runDbHandler :: AppState.AppState -> SQL.Mode -> Bool -> Bool -> DbHandler b -> Handler IO b
|
||||
runDbHandler appState mode authenticated prepared handler = do
|
||||
@@ -170,8 +168,8 @@ runDbHandler appState mode authenticated prepared handler = do
|
||||
|
||||
liftEither resp
|
||||
|
||||
handleRequest :: AuthResult -> AppConfig -> AppState.AppState -> Bool -> Bool -> ByteString -> PgVersion -> ApiRequest -> SchemaCache -> Handler IO Wai.Response
|
||||
handleRequest AuthResult{..} conf appState authenticated prepared jsonDbS pgVer apiReq@ApiRequest{..} sCache =
|
||||
handleRequest :: AuthResult -> AppConfig -> AppState.AppState -> Bool -> Bool -> PgVersion -> ApiRequest -> SchemaCache -> Handler IO Wai.Response
|
||||
handleRequest AuthResult{..} conf appState authenticated prepared pgVer apiReq@ApiRequest{..} sCache =
|
||||
case (iAction, iTarget) of
|
||||
(ActionRead headersOnly, TargetIdent identifier) -> do
|
||||
rPlan <- liftEither $ Plan.readPlan identifier conf sCache apiReq
|
||||
@@ -223,6 +221,6 @@ handleRequest AuthResult{..} conf appState authenticated prepared jsonDbS pgVer
|
||||
where
|
||||
runQuery mode query =
|
||||
runDbHandler appState mode authenticated prepared $ do
|
||||
Query.setPgLocals conf authClaims authRole apiReq jsonDbS pgVer
|
||||
Query.setPgLocals conf authClaims authRole apiReq pgVer
|
||||
Query.runPreReq conf
|
||||
query
|
||||
|
||||
@@ -7,7 +7,6 @@ module PostgREST.AppState
|
||||
, getConfig
|
||||
, getSchemaCache
|
||||
, getIsListenerOn
|
||||
, getJsonDbS
|
||||
, getMainThreadId
|
||||
, getPgVersion
|
||||
, getRetryNextIn
|
||||
@@ -20,7 +19,6 @@ module PostgREST.AppState
|
||||
, putConfig
|
||||
, putSchemaCache
|
||||
, putIsListenerOn
|
||||
, putJsonDbS
|
||||
, putPgVersion
|
||||
, putRetryNextIn
|
||||
, signalListener
|
||||
@@ -58,8 +56,6 @@ data AppState = AppState
|
||||
, statePgVersion :: IORef PgVersion
|
||||
-- | No schema cache at the start. Will be filled in by the connectionWorker
|
||||
, stateSchemaCache :: IORef (Maybe SchemaCache)
|
||||
-- | Cached SchemaCache in json
|
||||
, stateJsonDbS :: IORef ByteString
|
||||
-- | Binary semaphore to make sure just one connectionWorker can run at a time
|
||||
, stateWorkerSem :: MVar ()
|
||||
-- | Binary semaphore used to sync the listener(NOTIFY reload) with the connectionWorker.
|
||||
@@ -90,7 +86,6 @@ initWithPool pool conf = do
|
||||
appState <- AppState pool
|
||||
<$> newIORef minimumPgVersion -- assume we're in a supported version when starting, this will be corrected on a later step
|
||||
<*> newIORef Nothing
|
||||
<*> newIORef mempty
|
||||
<*> newEmptyMVar
|
||||
<*> newEmptyMVar
|
||||
<*> newIORef False
|
||||
@@ -146,12 +141,6 @@ getSchemaCache = readIORef . stateSchemaCache
|
||||
putSchemaCache :: AppState -> Maybe SchemaCache -> IO ()
|
||||
putSchemaCache appState = atomicWriteIORef (stateSchemaCache appState)
|
||||
|
||||
getJsonDbS :: AppState -> IO ByteString
|
||||
getJsonDbS = readIORef . stateJsonDbS
|
||||
|
||||
putJsonDbS :: AppState -> ByteString -> IO ()
|
||||
putJsonDbS appState = atomicWriteIORef (stateJsonDbS appState)
|
||||
|
||||
getWorkerSem :: AppState -> MVar ()
|
||||
getWorkerSem = stateWorkerSem
|
||||
|
||||
|
||||
@@ -34,8 +34,7 @@ import qualified PostgREST.SchemaCache.Proc as Proc
|
||||
|
||||
import Data.Scientific (FPFormat (..), formatScientific, isInteger)
|
||||
|
||||
import PostgREST.ApiRequest (ApiRequest (..),
|
||||
Target (..))
|
||||
import PostgREST.ApiRequest (ApiRequest (..))
|
||||
import PostgREST.ApiRequest.Preferences (PreferCount (..),
|
||||
PreferParameters (..),
|
||||
PreferTransaction (..),
|
||||
@@ -235,10 +234,10 @@ optionalRollback AppConfig{..} ApiRequest{..} = do
|
||||
|
||||
-- | Runs local (transaction scoped) GUCs for every request.
|
||||
setPgLocals :: AppConfig -> KM.KeyMap JSON.Value -> Text ->
|
||||
ApiRequest -> ByteString -> PgVersion -> DbHandler ()
|
||||
setPgLocals conf claims role req jsonDbS actualPgVersion = lift $
|
||||
ApiRequest -> PgVersion -> DbHandler ()
|
||||
setPgLocals conf claims role req actualPgVersion = lift $
|
||||
SQL.statement mempty $ SQL.dynamicallyParameterized
|
||||
("select " <> intercalateSnippet ", " (searchPathSql : roleSql ++ claimsSql ++ [methodSql, pathSql] ++ headersSql ++ cookiesSql ++ appSettingsSql ++ specSql))
|
||||
("select " <> intercalateSnippet ", " (searchPathSql : roleSql ++ claimsSql ++ [methodSql, pathSql] ++ headersSql ++ cookiesSql ++ appSettingsSql))
|
||||
HD.noResult (configDbPreparedStatements conf)
|
||||
where
|
||||
methodSql = setConfigLocal mempty ("request.method", iMethod req)
|
||||
@@ -257,9 +256,6 @@ setPgLocals conf claims role req jsonDbS actualPgVersion = lift $
|
||||
searchPathSql =
|
||||
let schemas = pgFmtIdentList (iSchema req : configDbExtraSearchPath conf) in
|
||||
setConfigLocal mempty ("search_path", schemas)
|
||||
specSql = case iTarget req of
|
||||
TargetProc{tpIsRootSpec=True} -> [setConfigLocal mempty ("request.spec", jsonDbS)]
|
||||
_ -> mempty
|
||||
usesLegacyGucs = configDbUseLegacyGucs conf && actualPgVersion < pgVersion140
|
||||
|
||||
unquoted :: JSON.Value -> Text
|
||||
|
||||
@@ -9,9 +9,7 @@ module PostgREST.Workers
|
||||
, runAdmin
|
||||
) where
|
||||
|
||||
import qualified Data.Aeson as JSON
|
||||
import qualified Data.ByteString as BS
|
||||
import qualified Data.ByteString.Lazy as LBS
|
||||
import qualified Data.Text as T
|
||||
import qualified Hasql.Notifications as SQL
|
||||
import qualified Hasql.Session as SQL
|
||||
@@ -179,8 +177,6 @@ loadSchemaCache appState = do
|
||||
|
||||
Right sCache -> do
|
||||
AppState.putSchemaCache appState (Just sCache)
|
||||
when (isJust configDbRootSpec) .
|
||||
AppState.putJsonDbS appState . LBS.toStrict $ JSON.encode sCache
|
||||
AppState.logWithZTime appState "Schema cache loaded"
|
||||
return SCLoaded
|
||||
|
||||
|
||||
@@ -9,8 +9,6 @@ import Test.Hspec.Wai.JSON
|
||||
|
||||
import Protolude hiding (get)
|
||||
|
||||
import SpecHelper
|
||||
|
||||
spec :: SpecWith ((), Application)
|
||||
spec =
|
||||
describe "root spec function" $ do
|
||||
@@ -22,9 +20,3 @@ spec =
|
||||
"info": {"title": "PostgREST API", "description": "This is a dynamic API generated by PostgREST"}
|
||||
}|]
|
||||
{ matchHeaders = ["Content-Type" <:> "application/openapi+json; charset=utf-8"] }
|
||||
|
||||
it "accepts application/json" $
|
||||
request methodGet "/"
|
||||
[("Accept", "application/json")] "" `shouldRespondWith`
|
||||
200
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
module Main where
|
||||
|
||||
import qualified Data.Aeson as JSON
|
||||
import qualified Hasql.Pool as P
|
||||
import qualified Hasql.Transaction.Sessions as HT
|
||||
|
||||
@@ -14,7 +13,6 @@ import PostgREST.Config (AppConfig (..))
|
||||
import PostgREST.Config.Database (queryPgVersion)
|
||||
import PostgREST.SchemaCache (querySchemaCache)
|
||||
import Protolude hiding (toList, toS)
|
||||
import Protolude.Conv (toS)
|
||||
import SpecHelper
|
||||
|
||||
import qualified PostgREST.AppState as AppState
|
||||
@@ -82,8 +80,6 @@ main = do
|
||||
appState <- AppState.initWithPool pool config
|
||||
AppState.putPgVersion appState actualPgVersion
|
||||
AppState.putSchemaCache appState (Just baseSchemaCache)
|
||||
when (isJust $ configDbRootSpec config) $
|
||||
AppState.putJsonDbS appState $ toS $ JSON.encode baseSchemaCache
|
||||
return ((), postgrest config appState $ pure ())
|
||||
|
||||
-- For tests that run with a different SchemaCache(depends on configSchemas)
|
||||
@@ -95,8 +91,6 @@ main = do
|
||||
appState <- AppState.initWithPool pool config
|
||||
AppState.putPgVersion appState actualPgVersion
|
||||
AppState.putSchemaCache appState (Just customSchemaCache)
|
||||
when (isJust $ configDbRootSpec config) $
|
||||
AppState.putJsonDbS appState $ toS $ JSON.encode baseSchemaCache
|
||||
return ((), postgrest config appState $ pure ())
|
||||
|
||||
let withApp = app testCfg
|
||||
|
||||
Vendored
+1
-13
@@ -1907,20 +1907,8 @@ openapi json = $$
|
||||
}
|
||||
}
|
||||
$$;
|
||||
accept text;
|
||||
begin
|
||||
accept = case when current_setting('server_version_num')::int >= 140000
|
||||
then current_setting('request.headers', true)::json->>'accept'
|
||||
else current_setting('request.header.accept', true)
|
||||
end;
|
||||
case accept
|
||||
when 'application/openapi+json' then
|
||||
return openapi;
|
||||
when 'application/json' then
|
||||
return (current_setting('request.spec', true)::json)->'dbRelationships'->0->0;
|
||||
else
|
||||
return openapi;
|
||||
end case;
|
||||
return openapi;
|
||||
end
|
||||
$_$ language plpgsql;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user