mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-17 12:31:52 +03:00
d52bfcda4e
* move user info related code to Hasura.User module
* the RFC #4120 implementation; insert permissions with admin secret
* revert back to old RoleName based schema maps
An attempt made to avoid duplication of schema contexts in types
if any role doesn't possess any admin secret specific schema
* fix compile errors in haskell test
* keep 'user_vars' for session variables in http-logs
* no-op refacto
* tests for admin only inserts
* update docs for admin only inserts
* updated CHANGELOG.md
* default behaviour when admin secret is not set
* fix x-hasura-role to X-Hasura-Role in pytests
* introduce effective timeout in actions async tests
* update docs for admin-secret not configured case
* Update docs/graphql/manual/api-reference/schema-metadata-api/permission.rst
Co-Authored-By: Marion Schleifer <marion@hasura.io>
* Apply suggestions from code review
Co-Authored-By: Marion Schleifer <marion@hasura.io>
* a complete iteration
backend insert permissions accessable via 'x-hasura-backend-privilege'
session variable
* console changes for backend-only permissions
* provide tooltip id; update labels and tooltips;
* requested changes
* requested changes
- remove className from Toggle component
- use appropriate function name (capitalizeFirstChar -> capitalize)
* use toggle props from definitelyTyped
* fix accidental commit
* Revert "introduce effective timeout in actions async tests"
This reverts commit b7a59c19d6
.
* generate complete schema for both 'default' and 'backend' sessions
* Apply suggestions from code review
Co-Authored-By: Marion Schleifer <marion@hasura.io>
* remove unnecessary import, export Toggle as is
* update session variable in tooltip
* 'x-hasura-use-backend-only-permissions' variable to switch
* update help texts
* update docs
* update docs
* update console help text
* regenerate package-lock
* serve no backend schema when backend_only: false and header set to true
- Few type name refactor as suggested by @0x777
* update CHANGELOG.md
* Update CHANGELOG.md
* Update CHANGELOG.md
* fix a merge bug where a certain entity didn't get removed
Co-authored-by: Marion Schleifer <marion@hasura.io>
Co-authored-by: Rishichandra Wawhal <rishi@hasura.io>
Co-authored-by: rikinsk <rikin.kachhia@gmail.com>
Co-authored-by: Tirumarai Selvan <tiru@hasura.io>
155 lines
5.7 KiB
Haskell
155 lines
5.7 KiB
Haskell
module Hasura.GraphQL.Schema.Merge
|
|
( mergeGCtx
|
|
, checkSchemaConflicts
|
|
, checkConflictingNode
|
|
) where
|
|
|
|
|
|
import Data.Maybe (maybeToList)
|
|
|
|
import qualified Data.HashMap.Strict as Map
|
|
import qualified Data.Text as T
|
|
import qualified Language.GraphQL.Draft.Syntax as G
|
|
|
|
import Hasura.GraphQL.Context
|
|
import Hasura.GraphQL.Validate.Types
|
|
import Hasura.Prelude
|
|
import Hasura.RQL.Types
|
|
|
|
mergeGCtx :: (MonadError QErr m) => GCtx -> GCtx -> m GCtx
|
|
mergeGCtx lGCtx rGCtx = do
|
|
checkSchemaConflicts lGCtx rGCtx
|
|
pure GCtx { _gTypes = mergedTypeMap
|
|
, _gFields = _gFields lGCtx <> _gFields rGCtx
|
|
, _gQueryRoot = mergedQueryRoot
|
|
, _gMutRoot = mergedMutationRoot
|
|
, _gSubRoot = mergedSubRoot
|
|
, _gOrdByCtx = _gOrdByCtx lGCtx <> _gOrdByCtx rGCtx
|
|
, _gQueryCtxMap = _gQueryCtxMap lGCtx <> _gQueryCtxMap rGCtx
|
|
, _gMutationCtxMap = _gMutationCtxMap lGCtx <> _gMutationCtxMap rGCtx
|
|
, _gInsCtxMap = _gInsCtxMap lGCtx <> _gInsCtxMap rGCtx
|
|
}
|
|
where
|
|
mergedQueryRoot = _gQueryRoot lGCtx <> _gQueryRoot rGCtx
|
|
mergedMutationRoot = _gMutRoot lGCtx <> _gMutRoot rGCtx
|
|
mergedSubRoot = _gSubRoot lGCtx <> _gSubRoot rGCtx
|
|
mergedTypeMap =
|
|
let mergedTypes = _gTypes lGCtx <> _gTypes rGCtx
|
|
modifyQueryRootField = Map.insert queryRootNamedType (TIObj mergedQueryRoot)
|
|
modifyMaybeRootField tyname maybeObj m = case maybeObj of
|
|
Nothing -> m
|
|
Just obj -> Map.insert tyname (TIObj obj) m
|
|
in modifyMaybeRootField subscriptionRootNamedType mergedSubRoot $
|
|
modifyMaybeRootField mutationRootNamedType mergedMutationRoot $
|
|
modifyQueryRootField mergedTypes
|
|
|
|
checkSchemaConflicts
|
|
:: (MonadError QErr m)
|
|
=> GCtx -> GCtx -> m ()
|
|
checkSchemaConflicts gCtx remoteCtx = do
|
|
let typeMap = _gTypes gCtx -- hasura typemap
|
|
-- check type conflicts
|
|
let hTypes = Map.elems typeMap
|
|
hTyNames = map G.unNamedType $ Map.keys typeMap
|
|
-- get the root names from the remote schema
|
|
rmQRootName = _otiName $ _gQueryRoot remoteCtx
|
|
rmMRootName = maybeToList $ _otiName <$> _gMutRoot remoteCtx
|
|
rmSRootName = maybeToList $ _otiName <$> _gSubRoot remoteCtx
|
|
rmRootNames = map G.unNamedType (rmQRootName:(rmMRootName ++ rmSRootName))
|
|
let rmTypes = Map.filterWithKey
|
|
(\k _ -> G.unNamedType k `notElem` builtinTy ++ rmRootNames)
|
|
$ _gTypes remoteCtx
|
|
|
|
isTyInfoSame ty = any (`tyinfoEq` ty) hTypes
|
|
-- name is same and structure is not same
|
|
isSame n ty = G.unNamedType n `elem` hTyNames &&
|
|
not (isTyInfoSame ty)
|
|
conflictedTypes = Map.filterWithKey isSame rmTypes
|
|
conflictedTyNames = map G.unNamedType $ Map.keys conflictedTypes
|
|
|
|
unless (Map.null conflictedTypes) $
|
|
throw400 RemoteSchemaConflicts $ tyMsg conflictedTyNames
|
|
|
|
-- check node conflicts
|
|
let rmQRoot = _otiFields $ _gQueryRoot remoteCtx
|
|
rmMRoot = _otiFields <$> _gMutRoot remoteCtx
|
|
rmRoots = filter (`notElem` builtinNodes ++ rmRootNames) . Map.keys <$>
|
|
mergeMaybeMaps (Just rmQRoot) rmMRoot
|
|
hQR = _otiFields <$>
|
|
(getObjTyM =<< Map.lookup hQRName typeMap)
|
|
hMR = _otiFields <$>
|
|
(getObjTyM =<< Map.lookup hMRName typeMap)
|
|
hRoots = Map.keys <$> mergeMaybeMaps hQR hMR
|
|
|
|
case (rmRoots, hRoots) of
|
|
(Just rmR, Just hR) -> do
|
|
let conflictedNodes = filter (`elem` hR) rmR
|
|
unless (null conflictedNodes) $
|
|
throw400 RemoteSchemaConflicts $ nodesMsg conflictedNodes
|
|
_ -> return ()
|
|
|
|
where
|
|
tyinfoEq a b = case (a, b) of
|
|
(TIScalar t1, TIScalar t2) -> typeEq t1 t2
|
|
(TIObj t1, TIObj t2) -> typeEq t1 t2
|
|
(TIEnum t1, TIEnum t2) -> typeEq t1 t2
|
|
(TIInpObj t1, TIInpObj t2) -> typeEq t1 t2
|
|
_ -> False
|
|
|
|
hQRName = queryRootNamedType
|
|
hMRName = mutationRootNamedType
|
|
tyMsg ty = "types: [ " <> namesToTxt ty <>
|
|
" ] have mismatch with current graphql schema. HINT: Types must be same."
|
|
nodesMsg n = "top-level nodes: [ " <> namesToTxt n <>
|
|
" ] already exist in current graphql schema. HINT: Top-level nodes can't be same."
|
|
namesToTxt = T.intercalate ", " . map G.unName
|
|
builtinNodes = ["__type", "__schema", "__typename"]
|
|
builtinTy = [ "__Directive"
|
|
, "__DirectiveLocation"
|
|
, "__EnumValue"
|
|
, "__Field"
|
|
, "__InputValue"
|
|
, "__Schema"
|
|
, "__Type"
|
|
, "__TypeKind"
|
|
, "Int"
|
|
, "Float"
|
|
, "String"
|
|
, "Boolean"
|
|
, "ID"
|
|
]
|
|
|
|
checkConflictingNode
|
|
:: (MonadError QErr m)
|
|
=> TypeMap
|
|
-- ^ See 'GCtx'.
|
|
-> G.Name
|
|
-> m ()
|
|
checkConflictingNode typeMap node = do
|
|
let hQR = _otiFields <$>
|
|
(getObjTyM =<< Map.lookup hQRName typeMap)
|
|
hMR = _otiFields <$>
|
|
(getObjTyM =<< Map.lookup hMRName typeMap)
|
|
hRoots = Map.keys <$> mergeMaybeMaps hQR hMR
|
|
case hRoots of
|
|
Just hR ->
|
|
when (node `elem` hR) $
|
|
throw400 RemoteSchemaConflicts msg
|
|
_ -> return ()
|
|
where
|
|
hQRName = queryRootNamedType
|
|
hMRName = mutationRootNamedType
|
|
msg = "node " <> G.unName node <>
|
|
" already exists in current graphql schema"
|
|
|
|
mergeMaybeMaps
|
|
:: (Eq k, Hashable k)
|
|
=> Maybe (Map.HashMap k v)
|
|
-> Maybe (Map.HashMap k v)
|
|
-> Maybe (Map.HashMap k v)
|
|
mergeMaybeMaps m1 m2 = case (m1, m2) of
|
|
(Nothing, Nothing) -> Nothing
|
|
(Just m1', Nothing) -> Just m1'
|
|
(Nothing, Just m2') -> Just m2'
|
|
(Just m1', Just m2') -> Just $ Map.union m1' m2'
|