mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 09:22:43 +03:00
dont set non-null constraint for manual object relationships (close #462)
This commit is contained in:
parent
85df9ac1e8
commit
cde559fe58
@ -99,7 +99,7 @@ isValidTableName = isValidName . qualTableToName
|
||||
isValidField :: FieldInfo -> Bool
|
||||
isValidField = \case
|
||||
FIColumn (PGColInfo col _ _) -> isColEligible col
|
||||
FIRelationship (RelInfo rn _ _ remTab _) -> isRelEligible rn remTab
|
||||
FIRelationship (RelInfo rn _ _ remTab _ _) -> isRelEligible rn remTab
|
||||
where
|
||||
isColEligible = isValidName . G.Name . getPGColTxt
|
||||
isRelEligible rn rt = isValidName (G.Name $ getRelTxt rn)
|
||||
@ -262,7 +262,7 @@ object_relationship: remote_table
|
||||
|
||||
-}
|
||||
mkRelFld :: RelInfo -> Bool -> ObjFldInfo
|
||||
mkRelFld (RelInfo rn rTy _ remTab _) isNullable = case rTy of
|
||||
mkRelFld (RelInfo rn rTy _ remTab _ isManual) isNullable = case rTy of
|
||||
ArrRel ->
|
||||
ObjFldInfo (Just "An array relationship") (G.Name $ getRelTxt rn)
|
||||
(fromInpValL $ mkSelArgs remTab)
|
||||
@ -272,7 +272,8 @@ mkRelFld (RelInfo rn rTy _ remTab _) isNullable = case rTy of
|
||||
Map.empty
|
||||
objRelTy
|
||||
where
|
||||
objRelTy = bool (G.toGT $ G.toNT relTabTy) (G.toGT relTabTy) isNullable
|
||||
objRelTy = bool (G.toGT $ G.toNT relTabTy) (G.toGT relTabTy) isObjRelNullable
|
||||
isObjRelNullable = isManual || isNullable
|
||||
relTabTy = mkTableTy remTab
|
||||
|
||||
{-
|
||||
@ -400,7 +401,7 @@ mkBoolExpInp tn fields =
|
||||
mkFldExpInp = \case
|
||||
Left (PGColInfo colName colTy _) ->
|
||||
mk (G.Name $ getPGColTxt colName) (mkCompExpTy colTy)
|
||||
Right (RelInfo relName _ _ remTab _, _, _, _) ->
|
||||
Right (RelInfo relName _ _ remTab _ _, _, _, _) ->
|
||||
mk (G.Name $ getRelTxt relName) (mkBoolExpTy remTab)
|
||||
|
||||
mkPGColInp :: PGColInfo -> InpValInfo
|
||||
|
@ -178,7 +178,7 @@ objRelP2Setup qt (RelDef rn ru _) = do
|
||||
(lCols, rCols) = unzip $ M.toList $ rmColumns rm
|
||||
deps = map (\c -> SchemaDependency (SOTableObj qt $ TOCol c) "lcol") lCols
|
||||
<> map (\c -> SchemaDependency (SOTableObj refqt $ TOCol c) "rcol") rCols
|
||||
return $ RelInfo rn ObjRel (zip lCols rCols) refqt deps
|
||||
return $ RelInfo rn ObjRel (zip lCols rCols) refqt deps True
|
||||
RUFKeyOn cn -> do
|
||||
res <- liftTx $ Q.catchE defaultTxErrorHandler $ fetchFKeyDetail cn
|
||||
case mapMaybe processRes res of
|
||||
@ -190,7 +190,7 @@ objRelP2Setup qt (RelDef rn ru _) = do
|
||||
]
|
||||
refqt = QualifiedTable refsn reftn
|
||||
void $ askTabInfo refqt
|
||||
return $ RelInfo rn ObjRel colMapping refqt deps
|
||||
return $ RelInfo rn ObjRel colMapping refqt deps False
|
||||
_ -> throw400 ConstraintError
|
||||
"more than one foreign key constraint exists on the given column"
|
||||
addFldToCache (fromRel rn) (FIRelationship relInfo) qt
|
||||
@ -273,7 +273,7 @@ arrRelP2Setup qt (RelDef rn ru _) = do
|
||||
(lCols, rCols) = unzip $ M.toList $ rmColumns rm
|
||||
deps = map (\c -> SchemaDependency (SOTableObj qt $ TOCol c) "lcol") lCols
|
||||
<> map (\c -> SchemaDependency (SOTableObj refqt $ TOCol c) "rcol") rCols
|
||||
return $ RelInfo rn ArrRel (zip lCols rCols) refqt deps
|
||||
return $ RelInfo rn ArrRel (zip lCols rCols) refqt deps True
|
||||
RUFKeyOn (ArrRelUsingFKeyOn refqt refCol) -> do
|
||||
let QualifiedTable refSn refTn = refqt
|
||||
res <- liftTx $ Q.catchE defaultTxErrorHandler $
|
||||
@ -285,7 +285,7 @@ arrRelP2Setup qt (RelDef rn ru _) = do
|
||||
let deps = [ SchemaDependency (SOTableObj refqt $ TOCons consName) "remote_fkey"
|
||||
, SchemaDependency (SOTableObj refqt $ TOCol refCol) "using_col"
|
||||
]
|
||||
return $ RelInfo rn ArrRel (map swap mapping) refqt deps
|
||||
return $ RelInfo rn ArrRel (map swap mapping) refqt deps False
|
||||
_ -> throw400 ConstraintError
|
||||
"more than one foreign key constraint exists on the given column"
|
||||
addFldToCache (fromRel rn) (FIRelationship relInfo) qt
|
||||
|
@ -90,7 +90,7 @@ convSelCol fieldInfoMap _ (SCExtRel rn malias selQ) = do
|
||||
let pgWhenRelErr = "only relationships can be expanded"
|
||||
relInfo <- withPathK "name" $
|
||||
askRelType fieldInfoMap rn pgWhenRelErr
|
||||
let (RelInfo _ _ _ relTab _) = relInfo
|
||||
let (RelInfo _ _ _ relTab _ _) = relInfo
|
||||
(rfim, rspi) <- fetchRelDet rn relTab
|
||||
resolvedSelQ <- resolveStar rfim rspi selQ
|
||||
return [ECRel rn malias resolvedSelQ]
|
||||
@ -345,7 +345,7 @@ convExtRel fieldInfoMap relName mAlias selQ prepValBuilder = do
|
||||
-- Point to the name key
|
||||
relInfo <- withPathK "name" $
|
||||
askRelType fieldInfoMap relName pgWhenRelErr
|
||||
let (RelInfo _ relTy colMapping relTab _) = relInfo
|
||||
let (RelInfo _ relTy colMapping relTab _ _) = relInfo
|
||||
(relCIM, relSPI) <- fetchRelDet relName relTab
|
||||
selectData <- case relTy of
|
||||
ObjRel ->
|
||||
|
@ -372,7 +372,7 @@ convColRhs bExpBuilder tableQual annVal = case annVal of
|
||||
-- And them all
|
||||
return $ AVCol pci $ foldr (S.BEBin S.AndOp) (S.BELit True) bExps
|
||||
|
||||
AVRel ri@(RelInfo _ _ colMapping relTN _) nesAnn fltr -> do
|
||||
AVRel ri@(RelInfo _ _ colMapping relTN _ _) nesAnn fltr -> do
|
||||
-- Convert the where clause on the relationship
|
||||
annRelBoolExp <- convBoolRhs bExpBuilder (S.mkQual relTN) nesAnn
|
||||
let backCompExp = foldr (S.BEBin S.AndOp) (S.BELit True) $
|
||||
@ -393,7 +393,7 @@ cColExp
|
||||
-> S.BoolExp
|
||||
cColExp annVal = case annVal of
|
||||
AVCol _ be -> be
|
||||
AVRel (RelInfo _ _ _ relTN _) nesAnn backCompExp -> do
|
||||
AVRel (RelInfo _ _ _ relTN _ _) nesAnn backCompExp -> do
|
||||
-- Convert the where clause on the relationship
|
||||
let annRelBoolExp = cBoolExp nesAnn
|
||||
innerBoolExp = S.BEBin S.AndOp backCompExp annRelBoolExp
|
||||
|
@ -210,11 +210,12 @@ getColInfos cols allColInfos = flip filter allColInfos $ \ci ->
|
||||
|
||||
data RelInfo
|
||||
= RelInfo
|
||||
{ riName :: !RelName
|
||||
, riType :: !RelType
|
||||
, riMapping :: ![(PGCol, PGCol)]
|
||||
, riRTable :: !QualifiedTable
|
||||
, riDeps :: ![SchemaDependency]
|
||||
{ riName :: !RelName
|
||||
, riType :: !RelType
|
||||
, riMapping :: ![(PGCol, PGCol)]
|
||||
, riRTable :: !QualifiedTable
|
||||
, riDeps :: ![SchemaDependency]
|
||||
, riIsManual :: !Bool
|
||||
} deriving (Show, Eq)
|
||||
|
||||
$(deriveToJSON (aesonDrop 2 snakeCase) ''RelInfo)
|
||||
|
@ -22,8 +22,8 @@ args:
|
||||
id SERIAL PRIMARY KEY,
|
||||
title TEXT,
|
||||
content TEXT,
|
||||
author_id INTEGER REFERENCES author(id),
|
||||
is_published BOOLEAN,
|
||||
author_id INTEGER NOT NULL REFERENCES author(id),
|
||||
is_published BOOLEAN NOT NULL default FALSE,
|
||||
published_on TIMESTAMP
|
||||
)
|
||||
- type: track_table
|
||||
@ -34,11 +34,24 @@ args:
|
||||
#Object relationship
|
||||
- type: create_object_relationship
|
||||
args:
|
||||
name: author_obj_rel_fk
|
||||
table: article
|
||||
name: author
|
||||
using:
|
||||
foreign_key_constraint_on: author_id
|
||||
|
||||
#Object relationship
|
||||
- type: create_object_relationship
|
||||
args:
|
||||
name: author_obj_rel_manual
|
||||
table: article
|
||||
using:
|
||||
manual_configuration:
|
||||
remote_table:
|
||||
schema: public
|
||||
name: author
|
||||
column_mapping:
|
||||
author_id: id
|
||||
|
||||
#Array relationship
|
||||
- type: create_array_relationship
|
||||
args:
|
||||
@ -55,7 +68,10 @@ args:
|
||||
table: article
|
||||
role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
columns:
|
||||
- id
|
||||
- title
|
||||
- content
|
||||
filter:
|
||||
$or:
|
||||
- author_id: X-HASURA-USER-ID
|
||||
@ -76,7 +92,8 @@ args:
|
||||
table: author
|
||||
role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
columns:
|
||||
- name
|
||||
filter:
|
||||
id: X-HASURA-USER-ID
|
||||
|
||||
|
40
server/tests-py/test_graphql_introspection.py
Normal file
40
server/tests-py/test_graphql_introspection.py
Normal file
@ -0,0 +1,40 @@
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
from validate import check_query_f
|
||||
|
||||
class TestGraphqlIntrospection:
|
||||
|
||||
def test_introspection(self, hge_ctx):
|
||||
with open(self.dir + "/introspection.yaml") as c:
|
||||
conf = yaml.load(c)
|
||||
code, resp = hge_ctx.anyq( conf['url'], conf['query'], {})
|
||||
assert code == 200
|
||||
hasArticle = False
|
||||
hasArticleAuthorFKRel = False
|
||||
hasArticleAuthorManualRel = False
|
||||
for t in resp['data']['__schema']['types']:
|
||||
if t['name'] == 'article':
|
||||
hasArticle = True
|
||||
for fld in t['fields']:
|
||||
if fld['name'] == 'author_obj_rel_manual':
|
||||
hasArticleAuthorManualRel = True
|
||||
assert fld['type']['kind'] == 'OBJECT'
|
||||
elif fld['name'] == 'author_obj_rel_fk':
|
||||
hasArticleAuthorFKRel = True
|
||||
assert fld['type']['kind'] == 'NON_NULL'
|
||||
assert hasArticle
|
||||
assert hasArticleAuthorFKRel
|
||||
assert hasArticleAuthorManualRel
|
||||
|
||||
def test_introspection_user(self, hge_ctx):
|
||||
check_query_f(hge_ctx, self.dir + "/introspection_user_role.yaml")
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def transact(self, request, hge_ctx):
|
||||
self.dir = "queries/graphql_introspection"
|
||||
st_code, resp = hge_ctx.v1q_f(self.dir + '/setup.yaml')
|
||||
assert st_code == 200, resp
|
||||
yield
|
||||
st_code, resp = hge_ctx.v1q_f(self.dir + '/teardown.yaml')
|
||||
assert st_code == 200, resp
|
@ -210,19 +210,3 @@ class TestGraphqlDelete:
|
||||
assert st_code == 200, resp
|
||||
|
||||
|
||||
class TestGraphqlIntrospection:
|
||||
|
||||
def test_introspection(self, hge_ctx):
|
||||
check_query_f(hge_ctx, self.dir + "/introspection.yaml")
|
||||
|
||||
def test_introspection_user(self, hge_ctx):
|
||||
check_query_f(hge_ctx, self.dir + "/introspection_user_role.yaml")
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def transact(self, request, hge_ctx):
|
||||
self.dir = "queries/graphql_introspection"
|
||||
st_code, resp = hge_ctx.v1q_f(self.dir + '/setup.yaml')
|
||||
assert st_code == 200, resp
|
||||
yield
|
||||
st_code, resp = hge_ctx.v1q_f(self.dir + '/teardown.yaml')
|
||||
assert st_code == 200, resp
|
||||
|
Loading…
Reference in New Issue
Block a user