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

Fix __typename Introspection not Working on Introspection Types #1201

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
61 changes: 47 additions & 14 deletions ballerina-tests/tests/10_introspection_queries.bal
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ isolated function testIntrospectionQueryWithMissingSelection() returns error? {
groups: ["introspection"]
}
isolated function testInvalidSchemaIntrospectionField() returns error? {
string graphqlUrl ="http://localhost:9091/records";
string graphqlUrl = "http://localhost:9091/records";
string document = "{ profile(id: 1) { name __schema { queryType { name } } } }";
json actualResult = check getJsonPayloadFromBadRequest(graphqlUrl, document);
json expectedResult = {
errors: [
{
message: string`Cannot query field "__schema" on type "Person".`,
message: string `Cannot query field "__schema" on type "Person".`,
locations: [
{
line: 1,
Expand All @@ -89,7 +89,7 @@ isolated function testInvalidSchemaIntrospectionField() returns error? {
groups: ["introspection"]
}
isolated function testQueryTypeIntrospection() returns error? {
string graphqlUrl ="http://localhost:9091/validation";
string graphqlUrl = "http://localhost:9091/validation";
string document = "{ __schema { queryType { kind fields { name } } } }";
json actualResult = check getJsonPayloadFromService(graphqlUrl, document);
json expectedResult = check getJsonContentFromFile("query_type_introspection.json");
Expand All @@ -100,7 +100,7 @@ isolated function testQueryTypeIntrospection() returns error? {
groups: ["introspection"]
}
isolated function testMutationTypeIntrospection() returns error? {
string graphqlUrl ="http://localhost:9091/mutations";
string graphqlUrl = "http://localhost:9091/mutations";
string document = "{ __schema { mutationType { kind fields { name } } } }";
json actualResult = check getJsonPayloadFromService(graphqlUrl, document);
json expectedResult = check getJsonContentFromFile("mutation_type_introspection.json");
Expand All @@ -122,7 +122,7 @@ isolated function testComplexIntrospectionQueryWithOtherFields() returns error?
groups: ["introspection"]
}
isolated function testEnumValueIntrospection() returns error? {
string graphqlUrl ="http://localhost:9092/service_objects";
string graphqlUrl = "http://localhost:9092/service_objects";
string document = "{ __schema { types { enumValues } } }";
json actualResult = check getJsonPayloadFromBadRequest(graphqlUrl, document);
json expectedResult = check getJsonContentFromFile("enum_value_introspection.json");
Expand All @@ -133,7 +133,7 @@ isolated function testEnumValueIntrospection() returns error? {
groups: ["introspection", "typename"]
}
isolated function testTypeNameIntrospectionOnOperation() returns error? {
string graphqlUrl ="http://localhost:9091/records";
string graphqlUrl = "http://localhost:9091/records";
string document = "{ __typename }";
json actualResult = check getJsonPayloadFromService(graphqlUrl, document);
json expectedResult = {
Expand All @@ -148,7 +148,7 @@ isolated function testTypeNameIntrospectionOnOperation() returns error? {
groups: ["introspection", "typename"]
}
isolated function testTypeNameIntrospectionOnRecordTypes() returns error? {
string graphqlUrl ="http://localhost:9091/records";
string graphqlUrl = "http://localhost:9091/records";
string document = "{ detective { __typename } }";
json actualResult = check getJsonPayloadFromService(graphqlUrl, document);
json expectedResult = {
Expand All @@ -165,7 +165,7 @@ isolated function testTypeNameIntrospectionOnRecordTypes() returns error? {
groups: ["introspection", "validation", "typename"]
}
isolated function testQueryingSubfieldsOnTypeName() returns error? {
string graphqlUrl ="http://localhost:9091/records";
string graphqlUrl = "http://localhost:9091/records";
string document = "{ detective { __typename { name } } }";
json actualResult = check getJsonPayloadFromBadRequest(graphqlUrl, document);
json expectedResult = {
Expand Down Expand Up @@ -243,13 +243,13 @@ isolated function testIntrospectionOnServiceWithInputObjects() returns error? {
groups: ["introspection", "typename", "validation"]
}
isolated function testTypeNameIntrospectionOnScalar() returns error? {
string graphqlUrl ="http://localhost:9091/validation";
string graphqlUrl = "http://localhost:9091/validation";
string document = "{ name { __typename } }";
json actualResult = check getJsonPayloadFromBadRequest(graphqlUrl, document);
json expectedResult = {
errors: [
{
message: string`Field "name" must not have a selection since type "String!" has no subfields.`,
message: string `Field "name" must not have a selection since type "String!" has no subfields.`,
locations: [
{
line: 1,
Expand Down Expand Up @@ -300,9 +300,9 @@ isolated function testTypeIntrospection() returns error? {
}
isolated function testTypeIntrospectionOnNonExistingType() returns error? {
string graphqlUrl = "http://localhost:9091/records";
string document = string`{ __type(name: "INVALID") { kind } }`;
string document = string `{ __type(name: "INVALID") { kind } }`;
json result = check getJsonPayloadFromService(graphqlUrl, document);
json expectedPayload = { data: { __type: null } };
json expectedPayload = {data: {__type: null}};
assertJsonValuesWithOrder(result, expectedPayload);
}

Expand All @@ -311,12 +311,12 @@ isolated function testTypeIntrospectionOnNonExistingType() returns error? {
}
isolated function testTypeIntrospectionWithoutFields() returns error? {
string graphqlUrl = "http://localhost:9091/records";
string document = string`{ __type(name: "Person") }`;
string document = string `{ __type(name: "Person") }`;
json result = check getJsonPayloadFromBadRequest(graphqlUrl, document);
json expectedPayload = {
errors: [
{
message: string`Field "__type" of type "__Type" must have a selection of subfields. Did you mean "__type { ... }"?`,
message: string `Field "__type" of type "__Type" must have a selection of subfields. Did you mean "__type { ... }"?`,
locations: [
{
line: 1,
Expand Down Expand Up @@ -419,3 +419,36 @@ isolated function testTypeIntrospectionWithAlias() returns error? {
json expectedPayload = check getJsonContentFromFile("type_introspection_with_alias.json");
assertJsonValuesWithOrder(actualPayload, expectedPayload);
}

@test:Config {
groups: ["introspection", "type", "typename"]
}
isolated function testTypeNameIntrospectionOnTypeRecord() returns error? {
string graphqlUrl = "http://localhost:9091/records";
string document = check getGraphQLDocumentFromFile("typename_introspection_on_type_record.graphql");
json actualPayload = check getJsonPayloadFromService(graphqlUrl, document);
json expectedPayload = check getJsonContentFromFile("typename_introspection_on_type_record.json");
assertJsonValuesWithOrder(actualPayload, expectedPayload);
}

@test:Config {
groups: ["introspection", "type", "typename"]
}
isolated function testTypeNameIntrospectionOnSchemaIntrospection() returns error? {
string graphqlUrl = "http://localhost:9091/validation";
string document = check getGraphQLDocumentFromFile("typename_introspection_on_schema_introspection.graphql");
json actualPayload = check getJsonPayloadFromService(graphqlUrl, document);
json expectedPayload = check getJsonContentFromFile("typename_introspection_on_schema_introspection.json");
assertJsonValuesWithOrder(actualPayload, expectedPayload);
}

@test:Config {
groups: ["introspection", "field", "typename"]
}
isolated function testTypeNameIntrospectionOnField() returns error? {
string graphqlUrl = "http://localhost:9091/records";
string document = check getGraphQLDocumentFromFile("typename_introspection_on_field.graphql");
json actualPayload = check getJsonPayloadFromService(graphqlUrl, document);
json expectedPayload = check getJsonContentFromFile("typename_introspection_on_field.json");
assertJsonValuesWithOrder(actualPayload, expectedPayload);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
__type(name: "Person") {
name
fields {
name
__typename
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
__schema {
types {
name
__typename
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
__type(name: "Person") {
__typename
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"data": {
"__type": {
"name": "Person",
"fields": [
{
"name": "name",
"__typename": "__Field"
},
{
"name": "age",
"__typename": "__Field"
},
{
"name": "address",
"__typename": "__Field"
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"data": {
"__schema": {
"types": [
{
"name": "__Field",
"__typename": "__Type"
},
{
"name": "__TypeKind",
"__typename": "__Type"
},
{
"name": "Query",
"__typename": "__Type"
},
{
"name": "__Schema",
"__typename": "__Type"
},
{
"name": "__Type",
"__typename": "__Type"
},
{
"name": "__EnumValue",
"__typename": "__Type"
},
{
"name": "__DirectiveLocation",
"__typename": "__Type"
},
{
"name": "String",
"__typename": "__Type"
},
{
"name": "__InputValue",
"__typename": "__Type"
},
{
"name": "Boolean",
"__typename": "__Type"
},
{
"name": "Int",
"__typename": "__Type"
},
{
"name": "__Directive",
"__typename": "__Type"
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"data": {
"__type": {
"__typename": "__Type"
}
}
}
5 changes: 5 additions & 0 deletions ballerina/introspection_executor.bal
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ class IntrospectionExecutor {
}

isolated function getValueFromFieldNode(parser:FieldNode fieldNode, map<anydata> parentValue, Data result) {
string fieldName = fieldNode.getName();
if fieldName == TYPE_NAME_FIELD {
result[fieldNode.getAlias()] = getTypeNameFromValue(parentValue);
return;
}
anydata fieldValue = parentValue.get(fieldNode.getName());
self.getFieldValue(fieldNode, fieldValue, result);
}
Expand Down
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Fixed
- [[#3865] Fix Incomplete Type Info Given in Compiler Errors Issued from GraphQL Compiler Plugin](https://github.com/ballerina-platform/ballerina-standard-library/issues/3865)
- [[#3721] Fix Passing Incorrect Values when a Resolver Method has an Enum as Input Parameter](https://github.com/ballerina-platform/ballerina-standard-library/issues/3721)
- [[#4038] Fix `__typename` Introspection not Working on Introspection Types](https://github.com/ballerina-platform/ballerina-standard-library/issues/4038)

### Changed
- [[#3430] Parallelise GraphQL Document Validation](https://github.com/ballerina-platform/ballerina-standard-library/issues/3430)
Expand Down