Skip to content

Commit 6a3eeee

Browse files
committed
Addressing polyglot exception reported by user
* Custom directives were not accounted for in the parser logic * Added test cases for custom directives * Custom directive parsing and translation support verified
1 parent 955ecab commit 6a3eeee

File tree

7 files changed

+267
-9
lines changed

7 files changed

+267
-9
lines changed

src/main/java/com/ebay/graphql/parser/GraphQLParser.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ protected final String getObjectTypeName(String currentLine) {
269269

270270
currentLine = currentLine.replaceFirst("^\\s*type\\s", "");
271271
currentLine = currentLine.replace("{", "");
272+
currentLine = currentLine.replaceAll("(@.*)\\s*$", "");
272273

273274
// Drop the interface if it exists
274275
if (currentLine.contains(IMPLEMENTS_KEYWORD)) {
@@ -299,11 +300,17 @@ protected final void processObject(GraphQLFile graphQLFile, GraphQLSchema schema
299300

300301
line = line.trim();
301302

303+
if (line.contains("@")) {
304+
line = line.replaceAll("(@.*)\\s*$", "");
305+
}
306+
302307
if (line.endsWith("!")) {
303308
nullable = false;
304309
line = line.substring(0, line.length() - 1);
305310
}
306311

312+
line = line.trim();
313+
307314
kvp = new FieldKeyValuePair(line);
308315
GraphQLType value = kvp.getValue();
309316
if (!nullable) {

src/main/java/com/ebay/graphql/parser/matcher/GraphQLMatcher.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ public enum LineType {
3939
QUERY("^\\s*(extend\\s)?type\\sQuery\\s*\\{\\s*$"),
4040
MUTATION("^\\s*(extend\\s)?type\\sMutation\\s*\\{\\s*$"),
4141
SUBSCRIPTION("^\\s*(extend\\s)?type\\sSubscription\\s*\\{\\s*$"),
42-
FIELD_INT("^\\s*([A-Za-z_])([A-Za-z0-9_]*)(\\(.*\\))?\\s*:\\s*Int!?\\s*$"),
43-
FIELD_FLOAT("^\\s*([A-Za-z_])([A-Za-z0-9_]*)(\\(.*\\))?\\s*:\\s*Float!?\\s*$"),
44-
FIELD_STRING("^\\s*([A-Za-z_])([A-Za-z0-9_]*)(\\(.*\\))?\\s*:\\s*String!?\\s*$"),
45-
FIELD_BOOLEAN("^\\s*([A-Za-z_])([A-Za-z0-9_]*)(\\(.*\\))?\\s*:\\s*Boolean!?\\s*$"),
46-
FIELD_ID("^\\s*([A-Za-z_])([A-Za-z0-9_]*)(\\(.*\\))?\\s*:\\s*ID!?\\s*$"),
47-
FIELD_REFERENCE("^\\s*([A-Za-z_])([A-Za-z0-9_]*)(\\(.*\\))?\\s*:\\s*([A-Za-z0-9_]+)!?\\s*$"),
48-
OBJECT_DEFINITION("^\\s*type\\s+([A-Za-z_])([A-Za-z0-9_]*)\\s*(implements.*)?\\s*\\{\\s*$"),
42+
FIELD_INT("^\\s*([A-Za-z_])([A-Za-z0-9_]*)(\\(.*\\))?\\s*:\\s*Int!?\\s*(@.*)?\\s*$"),
43+
FIELD_FLOAT("^\\s*([A-Za-z_])([A-Za-z0-9_]*)(\\(.*\\))?\\s*:\\s*Float!?\\s*(@.*)?\\s*$"),
44+
FIELD_STRING("^\\s*([A-Za-z_])([A-Za-z0-9_]*)(\\(.*\\))?\\s*:\\s*String!?\\s*(@.*)?\\s*$"),
45+
FIELD_BOOLEAN("^\\s*([A-Za-z_])([A-Za-z0-9_]*)(\\(.*\\))?\\s*:\\s*Boolean!?\\s*(@.*)?\\s*$"),
46+
FIELD_ID("^\\s*([A-Za-z_])([A-Za-z0-9_]*)(\\(.*\\))?\\s*:\\s*ID!?\\s*(@.*)?\\s*$"),
47+
FIELD_REFERENCE("^\\s*([A-Za-z_])([A-Za-z0-9_]*)(\\(.*\\))?\\s*:\\s*([A-Za-z0-9_]+)!?\\s*(@.*)?\\s*$"),
48+
OBJECT_DEFINITION("^\\s*type\\s+([A-Za-z_])([A-Za-z0-9_]*)\\s*(implements.*)?\\s*(@.*)?\\{\\s*$"),
4949
SCALAR_DEFINITION("^\\s*scalar\\s+([A-Za-z_])([A-Za-z0-9_]*)\\s?(@.*)?$"),
5050
UNION_DEFINITION("^\\s*union\\s+([A-Za-z_])([A-Za-z0-9_]*)\\s*=\\s*(([A-Za-z_])([A-Za-z0-9_]*)\\s?\\|?\\s?)*\\s*$"),
5151
UNION_MEMBER("^(\\s*\\|\\s*([A-Za-z_])([A-Za-z0-9_]*))+\\s*$"),

src/test/java/com/ebay/graphql/parser/GraphQLParserTest.java

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import java.util.Map;
1515
import java.util.Optional;
1616

17-
import org.hamcrest.Matchers;
1817
import org.testng.annotations.DataProvider;
1918
import org.testng.annotations.Test;
2019

@@ -562,7 +561,113 @@ public void cdcSchema() throws IOException, URISyntaxException {
562561
public void smeSchema() throws Exception {
563562
File file = getGraphQLResourceFile("com/ebay/graphql/erroreval/MultipleMutations.graphqls");
564563
GraphQLSchema actualSchema = parser.parseGraphQL(file);
565-
assertThat(actualSchema, is(notNullValue()));
564+
565+
// Define the expected parsed schema
566+
GraphQLSchema expectedSchema = new GraphQLSchema();
567+
568+
GraphQLReference preEnroll = new GraphQLReference("PreEnrollmentOutput");
569+
expectedSchema.addMutation("preEnroll", preEnroll);
570+
571+
GraphQLReference completeEnrollment = new GraphQLReference("EnrollmentOutput");
572+
expectedSchema.addMutation("completeEnrollment(completeInput: CompleteInput)", completeEnrollment);
573+
574+
GraphQLReference unEnroll = new GraphQLReference("EnrollmentStatusOutput");
575+
expectedSchema.addMutation("unEnroll", unEnroll);
576+
577+
GraphQLReference createEntryEventInput = new GraphQLReference("CreateEntryEventOutput");
578+
expectedSchema.addMutation("createEntryEvent( input: CreateEntryEventInput )", createEntryEventInput);
579+
580+
GraphQLReference copyEntryOutput = new GraphQLReference("CopyEntryOutput");
581+
expectedSchema.addMutation("copyEntry( input: CopyEntryInput! )", copyEntryOutput);
582+
583+
GraphQLReference bulkDeleteEntries = new GraphQLReference("BulkDeleteEntriesOutput");
584+
expectedSchema.addMutation("bulkDeleteEntries( input: BulkDeleteEntriesInput! )", bulkDeleteEntries);
585+
586+
GraphQLScalar dateTime = new GraphQLScalar(GraphQLScalarValue.STRING);
587+
expectedSchema.addType("DateTime", dateTime);
588+
589+
GraphQLScalar decimal = new GraphQLScalar(GraphQLScalarValue.STRING);
590+
expectedSchema.addType("Decimal", decimal);
591+
592+
GraphQLScalar voidScalar = new GraphQLScalar(GraphQLScalarValue.STRING);
593+
expectedSchema.addType("Void", voidScalar);
594+
595+
// Compare
596+
assertThat(actualSchema, is(equalTo(expectedSchema)));
597+
}
598+
599+
@Test
600+
public void polyglotExceptionSchema() throws Exception {
601+
File file = getGraphQLResourceFile("com/ebay/graphql/polyglotError/PolyglotException.graphqls");
602+
GraphQLSchema actualSchema = parser.parseGraphQL(file);
603+
604+
// Define the expected parsed schema
605+
GraphQLSchema expectedSchema = new GraphQLSchema();
606+
GraphQLReference disSpecificationOutput = new GraphQLReference("DisSpecificationOutput");
607+
disSpecificationOutput.makeNonNullable();
608+
expectedSchema.addQuery("DisSpecifications(input: SpecificationInput!)", disSpecificationOutput);
609+
610+
GraphQLScalar dateTime = new GraphQLScalar(GraphQLScalarValue.STRING);
611+
expectedSchema.addType("DateTime", dateTime);
612+
613+
GraphQLScalar decimal = new GraphQLScalar(GraphQLScalarValue.STRING);
614+
expectedSchema.addType("Decimal", decimal);
615+
616+
GraphQLObject disSpecificationOutputType = new GraphQLObject();
617+
GraphQLReference disSpecificationField = new GraphQLReference("DisSpecification");
618+
disSpecificationOutputType.addField("disSpecification", disSpecificationField);
619+
expectedSchema.addType("DisSpecificationOutput", disSpecificationOutputType);
620+
621+
GraphQLObject disSpecificationType = new GraphQLObject();
622+
GraphQLReference percentageOffField = new GraphQLReference("Decimal");
623+
disSpecificationType.addField("percentageOff", percentageOffField);
624+
GraphQLReference inCriteriaField = new GraphQLReference("InCriteria");
625+
disSpecificationType.addField("inCriteria", inCriteriaField);
626+
GraphQLReference paginationField = new GraphQLReference("Pagination");
627+
disSpecificationType.addField("pagination", paginationField);
628+
expectedSchema.addType("DisSpecification", disSpecificationType);
629+
630+
GraphQLObject inCriteriaType = new GraphQLObject();
631+
GraphQLReference typeField = new GraphQLReference("CriteriaType");
632+
typeField.makeNonNullable();
633+
inCriteriaType.addField("type", typeField);
634+
GraphQLList listingsField = new GraphQLList(new GraphQLReference("ProListing"), Dimensionality.SINGLE);
635+
listingsField.makeNonNullable();
636+
inCriteriaType.addField("listings", listingsField);
637+
expectedSchema.addType("InCriteria", inCriteriaType);
638+
639+
GraphQLObject proListingType = new GraphQLObject();
640+
GraphQLReference listingField = new GraphQLReference("Listing");
641+
listingField.makeNonNullable();
642+
proListingType.addField("listing", listingField);
643+
expectedSchema.addType("ProListing", proListingType);
644+
645+
GraphQLObject listingType = new GraphQLObject();
646+
GraphQLScalar idField = new GraphQLScalar(GraphQLScalarValue.ID);
647+
idField.makeNonNullable();
648+
listingType.addField("id", idField);
649+
GraphQLReference statusField = new GraphQLReference("Status");
650+
statusField.makeNonNullable();
651+
listingType.addField("status", statusField);
652+
expectedSchema.addType("Listing", listingType);
653+
654+
GraphQLObject paginationType = new GraphQLObject();
655+
GraphQLScalar nextCursorField = new GraphQLScalar(GraphQLScalarValue.STRING);
656+
paginationType.addField("nextCursor", nextCursorField);
657+
expectedSchema.addType("Pagination", paginationType);
658+
659+
GraphQLEnum criteriaTypeEnum = new GraphQLEnum();
660+
criteriaTypeEnum.addEnumValue("TYPE1");
661+
criteriaTypeEnum.addEnumValue("TYPE2");
662+
expectedSchema.addType("CriteriaType", criteriaTypeEnum);
663+
664+
GraphQLEnum statusEnum = new GraphQLEnum();
665+
statusEnum.addEnumValue("ACTIVE");
666+
statusEnum.addEnumValue("INACTIVE");
667+
expectedSchema.addType("Status", statusEnum);
668+
669+
// Compare
670+
assertThat(actualSchema, is(equalTo(expectedSchema)));
566671
}
567672

568673
// -------------------------------------------

src/test/java/com/ebay/graphql/parser/matcher/GraphQLMatcherTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ public Object[][] lineTypeMatchingData() {
108108

109109
// SCALAR_INT
110110
{ "foo: Int", LineType.FIELD_INT },
111+
{ "foo: Int @external", LineType.FIELD_INT },
111112
{ "Int: Int", LineType.FIELD_INT },
112113
{ " foo: Int", LineType.FIELD_INT },
113114
{ " foo: Int", LineType.FIELD_INT },
@@ -123,6 +124,7 @@ public Object[][] lineTypeMatchingData() {
123124

124125
// FIELD_INT
125126
{ "foo: Int!", LineType.FIELD_INT },
127+
{ "foo: Int! @external", LineType.FIELD_INT },
126128
{ "Int: Int!", LineType.FIELD_INT },
127129
{ " foo: Int!", LineType.FIELD_INT },
128130
{ " foo: Int!", LineType.FIELD_INT },
@@ -138,6 +140,7 @@ public Object[][] lineTypeMatchingData() {
138140

139141
// SCALAR_FLOAT
140142
{ "foo: Float", LineType.FIELD_FLOAT },
143+
{ "foo: Float @external", LineType.FIELD_FLOAT },
141144
{ "Float: Float", LineType.FIELD_FLOAT },
142145
{ " foo: Float", LineType.FIELD_FLOAT },
143146
{ " foo: Float", LineType.FIELD_FLOAT },
@@ -153,6 +156,7 @@ public Object[][] lineTypeMatchingData() {
153156

154157
// SCALAR_FLOAT_NON_NULLABLE
155158
{ "foo: Float!", LineType.FIELD_FLOAT },
159+
{ "foo: Float! @external", LineType.FIELD_FLOAT },
156160
{ "Float: Float!", LineType.FIELD_FLOAT },
157161
{ " foo: Float!", LineType.FIELD_FLOAT },
158162
{ " foo: Float!", LineType.FIELD_FLOAT },
@@ -168,6 +172,7 @@ public Object[][] lineTypeMatchingData() {
168172

169173
// SCALAR_STRING
170174
{ "foo: String", LineType.FIELD_STRING },
175+
{ "foo: String @external", LineType.FIELD_STRING },
171176
{ "String: String", LineType.FIELD_STRING },
172177
{ " foo: String", LineType.FIELD_STRING },
173178
{ " foo: String", LineType.FIELD_STRING },
@@ -183,6 +188,7 @@ public Object[][] lineTypeMatchingData() {
183188

184189
// FIELD_STRING
185190
{ "foo: String!", LineType.FIELD_STRING },
191+
{ "foo: String! @external", LineType.FIELD_STRING },
186192
{ "String: String!", LineType.FIELD_STRING },
187193
{ " foo: String!", LineType.FIELD_STRING },
188194
{ " foo: String!", LineType.FIELD_STRING },
@@ -198,6 +204,7 @@ public Object[][] lineTypeMatchingData() {
198204

199205
// SCALAR_BOOLEAN
200206
{ "foo: Boolean", LineType.FIELD_BOOLEAN },
207+
{ "foo: Boolean @external", LineType.FIELD_BOOLEAN },
201208
{ "Boolean: Boolean", LineType.FIELD_BOOLEAN },
202209
{ " foo: Boolean", LineType.FIELD_BOOLEAN },
203210
{ " foo: Boolean", LineType.FIELD_BOOLEAN },
@@ -213,6 +220,7 @@ public Object[][] lineTypeMatchingData() {
213220

214221
// FIELD_BOOLEAN
215222
{ "foo: Boolean!", LineType.FIELD_BOOLEAN },
223+
{ "foo: Boolean! @external", LineType.FIELD_BOOLEAN },
216224
{ "Boolean: Boolean!", LineType.FIELD_BOOLEAN },
217225
{ " foo: Boolean!", LineType.FIELD_BOOLEAN },
218226
{ " foo: Boolean!", LineType.FIELD_BOOLEAN },
@@ -228,6 +236,7 @@ public Object[][] lineTypeMatchingData() {
228236

229237
// FIELD_ID
230238
{ "foo: ID", LineType.FIELD_ID },
239+
{ "foo: ID @external", LineType.FIELD_ID },
231240
{ "ID: ID", LineType.FIELD_ID },
232241
{ " foo: ID", LineType.FIELD_ID },
233242
{ " foo: ID", LineType.FIELD_ID },
@@ -244,6 +253,7 @@ public Object[][] lineTypeMatchingData() {
244253
// FIELD_ID
245254
{ "foo: ID!", LineType.FIELD_ID },
246255
{ "ID: ID!", LineType.FIELD_ID },
256+
{ "ID: ID! @external", LineType.FIELD_ID },
247257
{ " foo: ID!", LineType.FIELD_ID },
248258
{ " foo: ID!", LineType.FIELD_ID },
249259
{ "foo: ID! ", LineType.FIELD_ID },
@@ -258,6 +268,8 @@ public Object[][] lineTypeMatchingData() {
258268

259269
// FIELD_REFERENCE
260270
{ "foo: uu_ID", LineType.FIELD_REFERENCE },
271+
{ "foo: uu_ID @external", LineType.FIELD_REFERENCE },
272+
{ "foo: uu_ID! @external", LineType.FIELD_REFERENCE },
261273
{ "foo: _UUID", LineType.FIELD_REFERENCE },
262274
{ "foo: _uu_id", LineType.FIELD_REFERENCE },
263275
{ "foo: URL", LineType.FIELD_REFERENCE },
@@ -288,6 +300,9 @@ public Object[][] lineTypeMatchingData() {
288300
{ "type Person implements Name {", LineType.OBJECT_DEFINITION },
289301
{ "type Person implements Name & Age {", LineType.OBJECT_DEFINITION },
290302
{ "type Person implements Name{", LineType.OBJECT_DEFINITION },
303+
{ "type DisSpecification @shareable {", LineType.OBJECT_DEFINITION },
304+
{ "type ProListing @key(fields: \"listing { id }\") {", LineType.OBJECT_DEFINITION },
305+
{ "type Listing @extends @key(fields: \"id status\") {", LineType.OBJECT_DEFINITION },
291306

292307
// SCALAR_DEFINITION
293308
{ "scalar UUID", LineType.SCALAR_DEFINITION },

src/test/java/com/ebay/graphql/transformer/GraphQLToJsonSchemaTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ public void beforeEachTest() {
4646
graphQLToJsonSchema = new GraphQLToJsonSchema(schema);
4747
}
4848

49+
@Test
50+
public void convertPolyglotCaseWithoutError() throws Exception {
51+
GraphQLParser parser = new GraphQLParser();
52+
File file = getGraphQLResourceFile("com/ebay/graphql/polyglotError/PolyglotException.graphqls");
53+
GraphQLSchema actualSchema = parser.parseGraphQL(file);
54+
55+
graphQLToJsonSchema = new GraphQLToJsonSchema(actualSchema);
56+
JsonNode actualNode = graphQLToJsonSchema.convertQuery("DisSpecifications(input: SpecificationInput!)");
57+
JsonNode expectedNode = loadResourceFile("/com/ebay/graphql/transformer/polyglotException.json");
58+
59+
assertThat(actualNode, is(equalTo(expectedNode)));
60+
}
61+
4962
@Test
5063
public void enumWithoutValues() throws Exception {
5164

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Define custom scalar types
2+
scalar DateTime
3+
scalar Decimal
4+
5+
# Define custom directives
6+
directive @key(fields: String!) repeatable on OBJECT | INTERFACE
7+
directive @shareable on OBJECT | FIELD_DEFINITION
8+
directive @extends on OBJECT | INTERFACE
9+
directive @external on FIELD_DEFINITION
10+
11+
# Base Query type
12+
type Query {
13+
DisSpecifications(input: SpecificationInput!): DisSpecificationOutput!
14+
}
15+
16+
# Types
17+
type DisSpecificationOutput @shareable {
18+
disSpecification: DisSpecification
19+
}
20+
21+
type DisSpecification @shareable {
22+
percentageOff: Decimal
23+
inCriteria: InCriteria
24+
pagination: Pagination
25+
}
26+
27+
type InCriteria @shareable {
28+
type: CriteriaType!
29+
listings: [ProListing]!
30+
}
31+
32+
type ProListing @key(fields: "listing { id }") {
33+
listing: Listing!
34+
}
35+
36+
type Listing @extends @key(fields: "id status") {
37+
id: ID! @external
38+
status: Status! @external
39+
}
40+
41+
type Pagination @shareable {
42+
nextCursor: String
43+
}
44+
45+
# Inputs
46+
input SpecificationInput {
47+
id: ID!
48+
queryCriteria: CriteriaInput!
49+
}
50+
51+
input CriteriaInput {
52+
listingPageInfoInput: PageInfoInput!
53+
discountId: ID!
54+
}
55+
56+
input PageInfoInput {
57+
pageCursor: String
58+
maxPageSize: Int!
59+
}
60+
61+
# Enums
62+
enum CriteriaType {
63+
TYPE1
64+
TYPE2
65+
}
66+
67+
enum Status {
68+
ACTIVE
69+
INACTIVE
70+
}

0 commit comments

Comments
 (0)