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

Server: support remote relationships to unions, interfaces and enum types #6080

Merged
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ This release contains the [PDV refactor (#4111)](https://github.com/hasura/graph
**NOTE:** If you have event triggers with names greater than 42 chars, then you should update their names to avoid running into Postgres identifier limit bug (#5786)
- server: validate remote schema queries (fixes #4143)
- server: fix issue with tracking custom functions that return `SETOF` materialized view (close #5294) (#5945)
- server: allow remote relationships with union, interface and enum type fields as well (fixes #5875) (#6080)
- console: allow user to cascade Postgres dependencies when dropping Postgres objects (close #5109) (#5248)
- console: mark inconsistent remote schemas in the UI (close #5093) (#5181)
- cli: add missing global flags for seed command (#5565)
Expand Down
9 changes: 4 additions & 5 deletions server/src-lib/Hasura/GraphQL/Schema/Remote.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ module Hasura.GraphQL.Schema.Remote

import Hasura.Prelude

import qualified Data.HashMap.Strict as Map
import qualified Data.HashMap.Strict.Extended as Map
import qualified Data.HashMap.Strict.InsOrd as OMap
import qualified Data.HashMap.Strict.InsOrd.Extended as OMap
import qualified Data.List.NonEmpty as NE

import Data.Foldable (sequenceA_)
Expand Down Expand Up @@ -277,9 +276,9 @@ remoteSchemaInterface schemaDoc defn@(G.InterfaceTypeDefinition description name
-- selection set provided
-- #1 of Note [Querying remote schema Interfaces]
commonInterfaceFields =
Map.elems $
Map.mapMaybe allTheSame $
Map.groupOn G._fName $
OMap.elems $
OMap.mapMaybe (allTheSame . toList) $
OMap.groupListWith G._fName $
concatMap (filter ((`elem` interfaceFieldNames) . G._fName) . snd) $
objNameAndFields

Expand Down
14 changes: 10 additions & 4 deletions server/src-lib/Hasura/RQL/DDL/RemoteRelationship/Validate.hs
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,15 @@ validateRemoteRelationship remoteRelationship remoteSchemaMap pgColumns = do
getObjTyInfoFromField schemaDoc field =
let baseTy = G.getBaseType (G._fldType field)
in lookupObject schemaDoc baseTy
isScalarType schemaDoc field =
isValidType schemaDoc field =
codingkarthik marked this conversation as resolved.
Show resolved Hide resolved
let baseTy = G.getBaseType (G._fldType field)
in isJust $ lookupScalar schemaDoc baseTy
in
case (lookupType schemaDoc baseTy) of
Just (G.TypeDefinitionScalar _) -> True
Just (G.TypeDefinitionInterface _) -> True
Just (G.TypeDefinitionUnion _) -> True
Just (G.TypeDefinitionEnum _) -> True
_ -> False
buildRelationshipTypeInfo pgColumnsVariablesMap schemaDoc (objTyInfo,(_,typeMap)) fieldCall = do
objFldDefinition <- lookupField (fcName fieldCall) objTyInfo
let providedArguments = getRemoteArguments $ fcArguments fieldCall
Expand All @@ -145,9 +151,9 @@ validateRemoteRelationship remoteRelationship remoteSchemaMap pgColumns = do
(newParamMap, newTypeMap) <- onLeft eitherParamAndTypeMap $ throwError
innerObjTyInfo <- onNothing (getObjTyInfoFromField schemaDoc objFldDefinition) $
bool (throwError $
InvalidType (G._fldType objFldDefinition) "only objects or scalar types expected")
(InvalidType (G._fldType objFldDefinition) "only output type is expected"))
(pure objTyInfo)
(isScalarType schemaDoc objFldDefinition)
(isValidType schemaDoc objFldDefinition)
pure
( innerObjTyInfo
, (newParamMap,newTypeMap))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
description: Simple remote relationship GraphQL query with union
url: /v1/graphql
status: 200
response:
data:
profiles:
- id: 1
getOccupationEnum: "SINGER"
- id: 2
getOccupationEnum: "ARTIST"
- id: 3
getOccupationEnum: "SINGER"
query:
query: |
query {
profiles {
id
getOccupationEnum
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
description: Simple remote relationship GraphQL query with union
url: /v1/graphql
status: 200
response:
data:
profiles:
- id: 1
searchUnion:
title: "Harry Porter and the prisoner of Azkaban"
- id: 2
searchUnion:
name: "J.K. Rowling"
- id: 3
searchUnion:
title: "Harry Porter and the philoshoper's stone"
query:
query: |
query {
profiles {
id
searchUnion {
... on Author {
name
}
... on Book {
title
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
type: create_remote_relationship
args:
name: getOccupationEnum
table: profiles
hasura_fields:
- name
remote_schema: my-remote-schema
remote_field:
getOccupation:
arguments:
name: $name
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
type: create_remote_relationship
args:
name: searchUnion
table: profiles
hasura_fields:
- id
remote_schema: my-remote-schema
remote_field:
search:
arguments:
id: $id
51 changes: 50 additions & 1 deletion server/tests-py/remote_schemas/nodejs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ const allMessages = [
{ id: 3, name: "alice", msg: "Another alice"},
];

const data = {
1: { title: 'Harry Porter and the prisoner of Azkaban' },
2: { name: 'J.K. Rowling' },
3: { title: "Harry Porter and the philoshoper's stone"}
}

const typeDefs = gql`

type User {
Expand All @@ -29,6 +35,16 @@ const typeDefs = gql`
errorMsg: String
}

union SearchResult = Book | Author

type Book {
title: String
}

type Author {
name: String
}

input MessageWhereInpObj {
id: IntCompareObj
name: StringCompareObj
Expand All @@ -49,13 +65,21 @@ const typeDefs = gql`
name: [String]
}

enum Occupation {
SINGER
COMEDIAN
ARTIST
}

type Query {
hello: String
messages(where: MessageWhereInpObj, includes: IncludeInpObj): [Message]
user(user_id: Int!): User
users(user_ids: [Int]!): [User]
message(id: Int!) : Message
communications(id: Int): [Communication]
search(id: Int!): SearchResult
getOccupation(name: String!): Occupation!
}
`;

Expand Down Expand Up @@ -116,7 +140,6 @@ const resolvers = {
}
}
},

Message: {
errorMsg : () => {
throw new ApolloError("intentional-error", "you asked for it");
Expand Down Expand Up @@ -190,12 +213,38 @@ const resolvers = {
}
return result;
},
search: (_, { id }) => {
return data[id]
},
getOccupation: (_, { name }) => {
switch (name) {
case "alice":
return 'SINGER'
case "bob":
return 'ARTIST'
default:
throw new ApolloError("invalid argument - " + name, "invalid ");
}
}
},
Communication: {
__resolveType(communication, context, info){
if(communication.name) {
return "Message";
}
return null;
}
},
SearchResult: {
__resolveType(obj, context, info) {
if(obj.name) {
return "Author";
}

if(obj.title) {
return "Book";
}

return null;
}
}
Expand Down
29 changes: 23 additions & 6 deletions server/tests-py/test_remote_relationships.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,14 @@ def test_create_valid(self, hge_ctx):
st_code, resp = hge_ctx.v1q_f(self.dir() + 'setup_remote_rel_joining_singleton_with_array.yaml')
assert st_code == 200, resp

# st_code, resp = hge_ctx.v1q_f(self.dir() + 'setup_remote_rel_with_interface.yaml')
# assert st_code == 200, resp
st_code, resp = hge_ctx.v1q_f(self.dir() + 'setup_remote_rel_with_interface.yaml')
assert st_code == 200, resp

st_code, resp = hge_ctx.v1q_f(self.dir() + 'setup_remote_rel_with_union.yaml')
assert st_code == 200, resp

st_code, resp = hge_ctx.v1q_f(self.dir() + 'setup_remote_rel_with_enum.yaml')
assert st_code == 200, resp

def test_create_invalid(self, hge_ctx):
st_code, resp = hge_ctx.v1q_f(self.dir() + 'setup_invalid_remote_rel_hasura_field.yaml')
Expand Down Expand Up @@ -202,10 +208,21 @@ def test_with_variables(self, hge_ctx):
# assert st_code == 200, resp
# check_query_f(hge_ctx, self.dir() + 'remote_rel_fragments.yaml')

# TODO: Support interface in remote relationships
# def test_with_interface(self, hge_ctx):
# check_query_f(hge_ctx, self.dir() + 'mixed_interface.yaml')
# check_query_f(hge_ctx, self.dir() + 'remote_rel_interface.yaml')
def test_with_interface(self, hge_ctx):
st_code, resp = hge_ctx.v1q_f(self.dir() + 'setup_remote_rel_with_interface.yaml')
assert st_code == 200, resp
check_query_f(hge_ctx, self.dir() + 'mixed_interface.yaml')
check_query_f(hge_ctx, self.dir() + 'remote_rel_interface.yaml')

def test_with_union(self, hge_ctx):
st_code, resp = hge_ctx.v1q_f(self.dir() + 'setup_remote_rel_with_union.yaml')
assert st_code == 200, resp
check_query_f(hge_ctx, self.dir() + 'remote_rel_union.yaml')

def test_with_enum(self, hge_ctx):
st_code, resp = hge_ctx.v1q_f(self.dir() + 'setup_remote_rel_with_enum.yaml')
assert st_code == 200, resp
check_query_f(hge_ctx, self.dir() + 'remote_rel_enum.yaml')

def test_with_errors(self, hge_ctx):
st_code, resp = hge_ctx.v1q_f(self.dir() + 'setup_remote_rel_basic.yaml')
Expand Down