From 843db9b7b3a1ee03da3819762ed257ac702ec9d2 Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Mon, 8 Aug 2022 18:29:02 +1000 Subject: [PATCH 1/6] Add parserOptions as config options Signed-off-by: Phillip Kruger --- .../graphql/cdi/config/ConfigKey.java | 7 ++- .../cdi/config/MicroProfileConfig.java | 63 +++++++++++++++++-- .../graphql/execution/ExecutionService.java | 22 +++++++ .../smallrye/graphql/spi/config/Config.java | 23 +++++++ 4 files changed, 109 insertions(+), 6 deletions(-) diff --git a/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/ConfigKey.java b/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/ConfigKey.java index a358ed73f..104001f39 100644 --- a/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/ConfigKey.java +++ b/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/ConfigKey.java @@ -22,5 +22,8 @@ public interface ConfigKey extends org.eclipse.microprofile.graphql.ConfigKey { public static final String ERROR_EXTENSION_FIELDS = "smallrye.graphql.errorExtensionFields"; public static final String FIELD_VISIBILITY = "smallrye.graphql.fieldVisibility"; public static final String UNWRAP_EXCEPTIONS = "smallrye.graphql.unwrapExceptions"; - -} + public static final String PARSER_CAPTURE_IGNORED_CHARS = "smallrye.graphql.parser.capture.ignoredChars"; + public static final String PARSER_CAPTURE_LINE_COMMENTS = "smallrye.graphql.parser.capture.lineComments"; + public static final String PARSER_CAPTURE_SOURCE_LOCATION = "smallrye.graphql.parser.capture.sourceLocation"; + public static final String PARSER_MAX_TOKENS = "smallrye.graphql.parser.maxTokens"; +} \ No newline at end of file diff --git a/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/MicroProfileConfig.java b/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/MicroProfileConfig.java index db4ce3837..353789c3b 100644 --- a/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/MicroProfileConfig.java +++ b/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/MicroProfileConfig.java @@ -34,6 +34,10 @@ public class MicroProfileConfig implements Config { private String fieldVisibility; private Optional> unwrapExceptions; private Optional> errorExtensionFields; + private Optional parserMaxTokens; + private Optional parserCaptureSourceLocation; + private Optional parserCaptureLineComments; + private Optional parserCaptureIgnoredChars; @Override public String getName() { @@ -168,6 +172,45 @@ public LogPayloadOption logPayload() { return logPayload; } + @Override + public Optional isParserCaptureIgnoredChars() { + if (parserCaptureIgnoredChars == null) { + org.eclipse.microprofile.config.Config microProfileConfig = ConfigProvider.getConfig(); + parserCaptureIgnoredChars = microProfileConfig.getOptionalValue(ConfigKey.PARSER_CAPTURE_IGNORED_CHARS, + Boolean.class); + } + return parserCaptureIgnoredChars; + } + + @Override + public Optional isParserCaptureLineComments() { + if (parserCaptureLineComments == null) { + org.eclipse.microprofile.config.Config microProfileConfig = ConfigProvider.getConfig(); + parserCaptureLineComments = microProfileConfig.getOptionalValue(ConfigKey.PARSER_CAPTURE_LINE_COMMENTS, + Boolean.class); + } + return parserCaptureLineComments; + } + + @Override + public Optional isParserCaptureSourceLocation() { + if (parserCaptureSourceLocation == null) { + org.eclipse.microprofile.config.Config microProfileConfig = ConfigProvider.getConfig(); + parserCaptureSourceLocation = microProfileConfig.getOptionalValue(ConfigKey.PARSER_CAPTURE_SOURCE_LOCATION, + Boolean.class); + } + return parserCaptureSourceLocation; + } + + @Override + public Optional getParserMaxTokens() { + if (parserMaxTokens == null) { + org.eclipse.microprofile.config.Config microProfileConfig = ConfigProvider.getConfig(); + parserMaxTokens = microProfileConfig.getOptionalValue(ConfigKey.PARSER_MAX_TOKENS, Integer.class); + } + return parserMaxTokens; + } + @Override public String getFieldVisibility() { if (fieldVisibility == null) { @@ -252,6 +295,22 @@ public void setLogPayload(LogPayloadOption logPayload) { this.logPayload = logPayload; } + public void setParserCaptureIgnoredChars(Optional parserCaptureIgnoredChars) { + this.parserCaptureIgnoredChars = parserCaptureIgnoredChars; + } + + public void setParserCaptureLineComments(Optional parserCaptureLineComments) { + this.parserCaptureLineComments = parserCaptureLineComments; + } + + public void setParserCaptureSourceLocation(Optional parserCaptureSourceLocation) { + this.parserCaptureSourceLocation = parserCaptureSourceLocation; + } + + public void setParserMaxTokens(Optional parserMaxTokens) { + this.parserMaxTokens = parserMaxTokens; + } + public void setFieldVisibility(String fieldVisibility) { this.fieldVisibility = fieldVisibility; } @@ -333,10 +392,6 @@ private Optional> mergeList(Optional> currentList, Opt } } - private String getStringConfigValue(String key) { - return getStringConfigValue(key, null); - } - private String getStringConfigValue(String key, String defaultValue) { return getConfigValue(key, String.class, defaultValue); } diff --git a/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java b/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java index 2a472139d..987b36048 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java @@ -23,6 +23,7 @@ import graphql.execution.ExecutionId; import graphql.execution.ExecutionStrategy; import graphql.execution.SubscriptionExecutionStrategy; +import graphql.parser.ParserOptions; import graphql.schema.GraphQLSchema; import io.smallrye.graphql.bootstrap.DataFetcherFactory; import io.smallrye.graphql.execution.context.SmallRyeContext; @@ -262,6 +263,27 @@ private GraphQL getGraphQL() { .subscriptionExecutionStrategy(new SubscriptionExecutionStrategy(new ExceptionHandler())); } + Config config = Config.get(); + if (config.hasParserOptions()) { + ParserOptions.Builder parserOptionsBuilder = ParserOptions.newParserOptions(); + if (config.isParserCaptureIgnoredChars().isPresent()) { + parserOptionsBuilder = parserOptionsBuilder + .captureIgnoredChars(config.isParserCaptureIgnoredChars().get()); + } + if (config.isParserCaptureLineComments().isPresent()) { + parserOptionsBuilder = parserOptionsBuilder + .captureLineComments(config.isParserCaptureLineComments().get()); + } + if (config.isParserCaptureSourceLocation().isPresent()) { + parserOptionsBuilder = parserOptionsBuilder + .captureSourceLocation(config.isParserCaptureSourceLocation().get()); + } + if (config.getParserMaxTokens().isPresent()) { + parserOptionsBuilder = parserOptionsBuilder.maxTokens(config.getParserMaxTokens().get()); + } + ParserOptions.setDefaultParserOptions(parserOptionsBuilder.build()); + } + // Allow custom extension graphqlBuilder = eventEmitter.fireBeforeGraphQLBuild(graphqlBuilder); diff --git a/server/implementation/src/main/java/io/smallrye/graphql/spi/config/Config.java b/server/implementation/src/main/java/io/smallrye/graphql/spi/config/Config.java index 6d4fa4b44..f02e3cfb4 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/spi/config/Config.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/spi/config/Config.java @@ -163,6 +163,29 @@ default LogPayloadOption logPayload() { return LogPayloadOption.off; } + default Optional isParserCaptureIgnoredChars() { + return Optional.empty(); + } + + default Optional isParserCaptureLineComments() { + return Optional.empty(); + } + + default Optional isParserCaptureSourceLocation() { + return Optional.empty(); + } + + default Optional getParserMaxTokens() { + return Optional.empty(); + } + + default boolean hasParserOptions() { + return isParserCaptureIgnoredChars().isPresent() + || isParserCaptureLineComments().isPresent() + || isParserCaptureSourceLocation().isPresent() + || getParserMaxTokens().isPresent(); + } + default String getFieldVisibility() { return FIELD_VISIBILITY_DEFAULT; } From 069eee8585a5fe215183c4fd08848db63915dc8f Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Mon, 8 Aug 2022 19:20:54 +1000 Subject: [PATCH 2/6] Upgrade to graphql-java 19 Signed-off-by: Phillip Kruger --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 834a9f849..ba7786304 100644 --- a/pom.xml +++ b/pom.xml @@ -29,13 +29,13 @@ 2.0.0-RC1 1.1.2 0.33.0 + 2.0.4 3.0.2 2.1.1 5.0.0 2.0.0 - - 18.1 + 19.0 1.9.3 4.3.3 From 853d1161eeecd5079e80c58ac0c136b091c0b50d Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Mon, 8 Aug 2022 19:58:27 +1000 Subject: [PATCH 3/6] Added more parser options from v19 Signed-off-by: Phillip Kruger --- .../graphql/cdi/config/ConfigKey.java | 1 + .../cdi/config/MicroProfileConfig.java | 15 ++++++ .../graphql/execution/ExecutionService.java | 50 +++++++++++-------- .../smallrye/graphql/spi/config/Config.java | 7 ++- .../META-INF/microprofile-config.properties | 3 +- 5 files changed, 53 insertions(+), 23 deletions(-) diff --git a/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/ConfigKey.java b/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/ConfigKey.java index 104001f39..cc579da0f 100644 --- a/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/ConfigKey.java +++ b/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/ConfigKey.java @@ -26,4 +26,5 @@ public interface ConfigKey extends org.eclipse.microprofile.graphql.ConfigKey { public static final String PARSER_CAPTURE_LINE_COMMENTS = "smallrye.graphql.parser.capture.lineComments"; public static final String PARSER_CAPTURE_SOURCE_LOCATION = "smallrye.graphql.parser.capture.sourceLocation"; public static final String PARSER_MAX_TOKENS = "smallrye.graphql.parser.maxTokens"; + public static final String PARSER_MAX_WHITESPACE_TOKENS = "smallrye.graphql.parser.maxWhitespaceTokens"; } \ No newline at end of file diff --git a/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/MicroProfileConfig.java b/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/MicroProfileConfig.java index 353789c3b..ab8e5c115 100644 --- a/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/MicroProfileConfig.java +++ b/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/MicroProfileConfig.java @@ -35,6 +35,7 @@ public class MicroProfileConfig implements Config { private Optional> unwrapExceptions; private Optional> errorExtensionFields; private Optional parserMaxTokens; + private Optional parserMaxWhitespaceTokens; private Optional parserCaptureSourceLocation; private Optional parserCaptureLineComments; private Optional parserCaptureIgnoredChars; @@ -211,6 +212,16 @@ public Optional getParserMaxTokens() { return parserMaxTokens; } + @Override + public Optional getParserMaxWhitespaceTokens() { + if (parserMaxWhitespaceTokens == null) { + org.eclipse.microprofile.config.Config microProfileConfig = ConfigProvider.getConfig(); + parserMaxWhitespaceTokens = microProfileConfig.getOptionalValue(ConfigKey.PARSER_MAX_WHITESPACE_TOKENS, + Integer.class); + } + return parserMaxWhitespaceTokens; + } + @Override public String getFieldVisibility() { if (fieldVisibility == null) { @@ -311,6 +322,10 @@ public void setParserMaxTokens(Optional parserMaxTokens) { this.parserMaxTokens = parserMaxTokens; } + public void setParserMaxWhitespaceTokens(Optional parserMaxWhitespaceTokens) { + this.parserMaxWhitespaceTokens = parserMaxWhitespaceTokens; + } + public void setFieldVisibility(String fieldVisibility) { this.fieldVisibility = fieldVisibility; } diff --git a/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java b/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java index 987b36048..63e8124c7 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java @@ -141,6 +141,8 @@ public void execute(JsonObject jsonInput, Map context, Execution GraphQL g = getGraphQL(); if (g != null) { + setParserOptions(); + // Query Builder executionBuilder = ExecutionInput.newExecutionInput() .query(query) @@ -263,27 +265,6 @@ private GraphQL getGraphQL() { .subscriptionExecutionStrategy(new SubscriptionExecutionStrategy(new ExceptionHandler())); } - Config config = Config.get(); - if (config.hasParserOptions()) { - ParserOptions.Builder parserOptionsBuilder = ParserOptions.newParserOptions(); - if (config.isParserCaptureIgnoredChars().isPresent()) { - parserOptionsBuilder = parserOptionsBuilder - .captureIgnoredChars(config.isParserCaptureIgnoredChars().get()); - } - if (config.isParserCaptureLineComments().isPresent()) { - parserOptionsBuilder = parserOptionsBuilder - .captureLineComments(config.isParserCaptureLineComments().get()); - } - if (config.isParserCaptureSourceLocation().isPresent()) { - parserOptionsBuilder = parserOptionsBuilder - .captureSourceLocation(config.isParserCaptureSourceLocation().get()); - } - if (config.getParserMaxTokens().isPresent()) { - parserOptionsBuilder = parserOptionsBuilder.maxTokens(config.getParserMaxTokens().get()); - } - ParserOptions.setDefaultParserOptions(parserOptionsBuilder.build()); - } - // Allow custom extension graphqlBuilder = eventEmitter.fireBeforeGraphQLBuild(graphqlBuilder); @@ -295,4 +276,31 @@ private GraphQL getGraphQL() { return this.graphQL; } + + private void setParserOptions() { + Config config = Config.get(); + if (config.hasParserOptions()) { + ParserOptions.Builder parserOptionsBuilder = ParserOptions.newParserOptions(); + if (config.isParserCaptureIgnoredChars().isPresent()) { + parserOptionsBuilder = parserOptionsBuilder + .captureIgnoredChars(config.isParserCaptureIgnoredChars().get()); + } + if (config.isParserCaptureLineComments().isPresent()) { + parserOptionsBuilder = parserOptionsBuilder + .captureLineComments(config.isParserCaptureLineComments().get()); + } + if (config.isParserCaptureSourceLocation().isPresent()) { + parserOptionsBuilder = parserOptionsBuilder + .captureSourceLocation(config.isParserCaptureSourceLocation().get()); + } + if (config.getParserMaxTokens().isPresent()) { + parserOptionsBuilder = parserOptionsBuilder.maxTokens(config.getParserMaxTokens().get()); + } + if (config.getParserMaxWhitespaceTokens().isPresent()) { + parserOptionsBuilder = parserOptionsBuilder + .maxWhitespaceTokens(config.getParserMaxWhitespaceTokens().get()); + } + ParserOptions.setDefaultParserOptions(parserOptionsBuilder.build()); + } + } } diff --git a/server/implementation/src/main/java/io/smallrye/graphql/spi/config/Config.java b/server/implementation/src/main/java/io/smallrye/graphql/spi/config/Config.java index f02e3cfb4..6576a6a17 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/spi/config/Config.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/spi/config/Config.java @@ -179,11 +179,16 @@ default Optional getParserMaxTokens() { return Optional.empty(); } + default Optional getParserMaxWhitespaceTokens() { + return Optional.empty(); + } + default boolean hasParserOptions() { return isParserCaptureIgnoredChars().isPresent() || isParserCaptureLineComments().isPresent() || isParserCaptureSourceLocation().isPresent() - || getParserMaxTokens().isPresent(); + || getParserMaxTokens().isPresent() + || getParserMaxWhitespaceTokens().isPresent(); } default String getFieldVisibility() { diff --git a/server/tck/src/test/resources/META-INF/microprofile-config.properties b/server/tck/src/test/resources/META-INF/microprofile-config.properties index e8ffdb645..ad7b91b3f 100644 --- a/server/tck/src/test/resources/META-INF/microprofile-config.properties +++ b/server/tck/src/test/resources/META-INF/microprofile-config.properties @@ -5,4 +5,5 @@ smallrye.graphql.logPayload=true mp.graphql.showErrorMessage=java.security.AccessControlException,io.smallrye.graphql.test.apps.exceptionlist.* mp.graphql.hideErrorMessage=java.io.IOException,io.smallrye.graphql.test.apps.exceptionlist.* -smallrye.graphql.errorExtensionFields=exception,classification,code,description,validationErrorType,queryPath \ No newline at end of file +smallrye.graphql.errorExtensionFields=exception,classification,code,description,validationErrorType,queryPath +smallrye.graphql.parser.capture.sourceLocation=false \ No newline at end of file From bf997265e57f650b0bd9b369ca6c4f7866f85ee8 Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Tue, 9 Aug 2022 10:52:49 +1000 Subject: [PATCH 4/6] Override TCK with validation messages Signed-off-by: Phillip Kruger --- .../createNewNullNamedHero/output2.json | 14 ++++++++++++++ .../overrides/createNewUnnamedHero/output2.json | 14 ++++++++++++++ .../overrides/invalidDataTypeValue/output3.json | 16 ++++++++++++++++ .../overrides/invalidEnumValue/output2.json | 14 ++++++++++++++ .../invalidLocalDateFormattedValue/output3.json | 16 ++++++++++++++++ .../output3.json | 16 ++++++++++++++++ .../invalidLocalDateTimeValue/output3.json | 16 ++++++++++++++++ .../overrides/invalidLocalDateValue/output3.json | 16 ++++++++++++++++ .../invalidLocalTimeFormattedValue/output3.json | 16 ++++++++++++++++ .../overrides/invalidLocalTimeValue/output3.json | 16 ++++++++++++++++ .../overrides/unknownField/output2.json | 14 ++++++++++++++ .../overrides/unknownMutation/output2.json | 14 ++++++++++++++ .../overrides/unknownQuery/output2.json | 14 ++++++++++++++ 13 files changed, 196 insertions(+) create mode 100644 server/tck/src/test/resources/overrides/createNewNullNamedHero/output2.json create mode 100644 server/tck/src/test/resources/overrides/createNewUnnamedHero/output2.json create mode 100644 server/tck/src/test/resources/overrides/invalidDataTypeValue/output3.json create mode 100644 server/tck/src/test/resources/overrides/invalidEnumValue/output2.json create mode 100644 server/tck/src/test/resources/overrides/invalidLocalDateFormattedValue/output3.json create mode 100644 server/tck/src/test/resources/overrides/invalidLocalDateTimeFormattedValue/output3.json create mode 100644 server/tck/src/test/resources/overrides/invalidLocalDateTimeValue/output3.json create mode 100644 server/tck/src/test/resources/overrides/invalidLocalDateValue/output3.json create mode 100644 server/tck/src/test/resources/overrides/invalidLocalTimeFormattedValue/output3.json create mode 100644 server/tck/src/test/resources/overrides/invalidLocalTimeValue/output3.json create mode 100644 server/tck/src/test/resources/overrides/unknownField/output2.json create mode 100644 server/tck/src/test/resources/overrides/unknownMutation/output2.json create mode 100644 server/tck/src/test/resources/overrides/unknownQuery/output2.json diff --git a/server/tck/src/test/resources/overrides/createNewNullNamedHero/output2.json b/server/tck/src/test/resources/overrides/createNewNullNamedHero/output2.json new file mode 100644 index 000000000..068ed46a7 --- /dev/null +++ b/server/tck/src/test/resources/overrides/createNewNullNamedHero/output2.json @@ -0,0 +1,14 @@ +{ + "errors": [ + { + "message": "Validation error (WrongType@[createNewHero]) : argument 'hero.name' with value 'NullValue{}' must not be null", + "locations": [ + { + "line": 2, + "column": 19 + } + ] + } + ], + "data": null +} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/createNewUnnamedHero/output2.json b/server/tck/src/test/resources/overrides/createNewUnnamedHero/output2.json new file mode 100644 index 000000000..ea8331aa7 --- /dev/null +++ b/server/tck/src/test/resources/overrides/createNewUnnamedHero/output2.json @@ -0,0 +1,14 @@ +{ + "errors": [ + { + "message": "Validation error (WrongType@[createNewHero]) : argument 'hero' with value 'ObjectValue{objectFields=[ObjectField{name='realName', value=StringValue{value='John Smith'}}]}' is missing required fields '[name]'", + "locations": [ + { + "line": 2, + "column": 19 + } + ] + } + ], + "data": null +} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/invalidDataTypeValue/output3.json b/server/tck/src/test/resources/overrides/invalidDataTypeValue/output3.json new file mode 100644 index 000000000..5f9b23eab --- /dev/null +++ b/server/tck/src/test/resources/overrides/invalidDataTypeValue/output3.json @@ -0,0 +1,16 @@ +{ + "errors": [ + { + "message": "argument 'powerLevel' with value 'StringValue{value='Unlimited'}' is not a valid 'Int'", + "locations": [ + { + "line": 2, + "column": 37 + } + ] + } + ], + "data": { + "updateItemPowerLevel": null + } +} diff --git a/server/tck/src/test/resources/overrides/invalidEnumValue/output2.json b/server/tck/src/test/resources/overrides/invalidEnumValue/output2.json new file mode 100644 index 000000000..a667fb5d6 --- /dev/null +++ b/server/tck/src/test/resources/overrides/invalidEnumValue/output2.json @@ -0,0 +1,14 @@ +{ + "errors": [ + { + "message": "Validation error (WrongType@[createNewHero]) : argument 'hero.tshirtSize' with value 'EnumValue{name='XLTall'}' is not a valid 'ShirtSize' - Expected enum literal value not in allowable values - 'EnumValue{name='XLTall'}'.", + "locations": [ + { + "line": 3, + "column": 19 + } + ] + } + ], + "data": null +} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/invalidLocalDateFormattedValue/output3.json b/server/tck/src/test/resources/overrides/invalidLocalDateFormattedValue/output3.json new file mode 100644 index 000000000..4fcff2375 --- /dev/null +++ b/server/tck/src/test/resources/overrides/invalidLocalDateFormattedValue/output3.json @@ -0,0 +1,16 @@ +{ + "errors": [ + { + "message": "argument 'date' with value 'StringValue{value='Today'}' is not a valid 'Date'", + "locations": [ + { + "line": 2, + "column": 49 + } + ] + } + ], + "data": { + "checkInWithCorrectDateFormat": null + } +} diff --git a/server/tck/src/test/resources/overrides/invalidLocalDateTimeFormattedValue/output3.json b/server/tck/src/test/resources/overrides/invalidLocalDateTimeFormattedValue/output3.json new file mode 100644 index 000000000..34be441fa --- /dev/null +++ b/server/tck/src/test/resources/overrides/invalidLocalDateTimeFormattedValue/output3.json @@ -0,0 +1,16 @@ +{ + "errors": [ + { + "message": "argument 'dateTime' with value 'StringValue{value='Today'}' is not a valid 'DateTime'", + "locations": [ + { + "line": 2, + "column": 48 + } + ] + } + ], + "data": { + "battleWithCorrectDateFormat": null + } +} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/invalidLocalDateTimeValue/output3.json b/server/tck/src/test/resources/overrides/invalidLocalDateTimeValue/output3.json new file mode 100644 index 000000000..af2c0dc4e --- /dev/null +++ b/server/tck/src/test/resources/overrides/invalidLocalDateTimeValue/output3.json @@ -0,0 +1,16 @@ +{ + "errors": [ + { + "message": "argument 'dateTime' with value 'StringValue{value='Today'}' is not a valid 'DateTime'", + "locations": [ + { + "line": 2, + "column": 27 + } + ] + } + ], + "data": { + "battle": null + } +} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/invalidLocalDateValue/output3.json b/server/tck/src/test/resources/overrides/invalidLocalDateValue/output3.json new file mode 100644 index 000000000..49fac88d8 --- /dev/null +++ b/server/tck/src/test/resources/overrides/invalidLocalDateValue/output3.json @@ -0,0 +1,16 @@ +{ + "errors": [ + { + "message": "argument 'date' with value 'StringValue{value='Today'}' is not a valid 'Date'", + "locations": [ + { + "line": 2, + "column": 28 + } + ] + } + ], + "data": { + "checkIn": null + } +} diff --git a/server/tck/src/test/resources/overrides/invalidLocalTimeFormattedValue/output3.json b/server/tck/src/test/resources/overrides/invalidLocalTimeFormattedValue/output3.json new file mode 100644 index 000000000..dbd808830 --- /dev/null +++ b/server/tck/src/test/resources/overrides/invalidLocalTimeFormattedValue/output3.json @@ -0,0 +1,16 @@ +{ + "errors": [ + { + "message": "argument 'time' with value 'StringValue{value='Today'}' is not a valid 'Time'", + "locations": [ + { + "line": 2, + "column": 57 + } + ] + } + ], + "data": { + "startPatrollingWithCorrectDateFormat": null + } +} diff --git a/server/tck/src/test/resources/overrides/invalidLocalTimeValue/output3.json b/server/tck/src/test/resources/overrides/invalidLocalTimeValue/output3.json new file mode 100644 index 000000000..78d6bfd37 --- /dev/null +++ b/server/tck/src/test/resources/overrides/invalidLocalTimeValue/output3.json @@ -0,0 +1,16 @@ +{ + "errors": [ + { + "message": "argument 'time' with value 'StringValue{value='Today'}' is not a valid 'Time'", + "locations": [ + { + "line": 2, + "column": 36 + } + ] + } + ], + "data": { + "startPatrolling": null + } +} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/unknownField/output2.json b/server/tck/src/test/resources/overrides/unknownField/output2.json new file mode 100644 index 000000000..6715517fc --- /dev/null +++ b/server/tck/src/test/resources/overrides/unknownField/output2.json @@ -0,0 +1,14 @@ +{ + "errors": [ + { + "message": "Validation error (FieldUndefined@[allHeroes/weaknesses]) : Field 'weaknesses' in type 'SuperHero' is undefined", + "locations": [ + { + "line": 4, + "column": 5 + } + ] + } + ], + "data": null +} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/unknownMutation/output2.json b/server/tck/src/test/resources/overrides/unknownMutation/output2.json new file mode 100644 index 000000000..c63695ca0 --- /dev/null +++ b/server/tck/src/test/resources/overrides/unknownMutation/output2.json @@ -0,0 +1,14 @@ +{ + "errors": [ + { + "message": "Validation error (FieldUndefined@[createNewHeroCat]) : Field 'createNewHeroCat' in type 'Mutation' is undefined", + "locations": [ + { + "line": 2, + "column": 5 + } + ] + } + ], + "data": null +} \ No newline at end of file diff --git a/server/tck/src/test/resources/overrides/unknownQuery/output2.json b/server/tck/src/test/resources/overrides/unknownQuery/output2.json new file mode 100644 index 000000000..474c2b5d5 --- /dev/null +++ b/server/tck/src/test/resources/overrides/unknownQuery/output2.json @@ -0,0 +1,14 @@ +{ + "errors": [ + { + "message": "Validation error (FieldUndefined@[allHeroesWhoLikeIceCream]) : Field 'allHeroesWhoLikeIceCream' in type 'Query' is undefined", + "locations": [ + { + "line": 2, + "column": 3 + } + ] + } + ], + "data": null +} From b4261b1c49fbbe462fd906289591103c305e902e Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Tue, 9 Aug 2022 14:24:08 +1000 Subject: [PATCH 5/6] Added queryComplexityInstrumentation and queryDepthInstrumentation as config options Signed-off-by: Phillip Kruger --- .../graphql/cdi/config/ConfigKey.java | 3 ++ .../cdi/config/MicroProfileConfig.java | 30 +++++++++++++++++++ .../graphql/execution/ExecutionService.java | 19 +++++++++--- .../smallrye/graphql/spi/config/Config.java | 8 +++++ 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/ConfigKey.java b/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/ConfigKey.java index cc579da0f..b97c93d13 100644 --- a/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/ConfigKey.java +++ b/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/ConfigKey.java @@ -27,4 +27,7 @@ public interface ConfigKey extends org.eclipse.microprofile.graphql.ConfigKey { public static final String PARSER_CAPTURE_SOURCE_LOCATION = "smallrye.graphql.parser.capture.sourceLocation"; public static final String PARSER_MAX_TOKENS = "smallrye.graphql.parser.maxTokens"; public static final String PARSER_MAX_WHITESPACE_TOKENS = "smallrye.graphql.parser.maxWhitespaceTokens"; + public static final String INSTRUMENTATION_QUERY_COMPLEXITY = "smallrye.graphql.instrumentation.queryComplexity"; + public static final String INSTRUMENTATION_QUERY_DEPTH = "smallrye.graphql.instrumentation.queryDepth"; + } \ No newline at end of file diff --git a/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/MicroProfileConfig.java b/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/MicroProfileConfig.java index ab8e5c115..ea1623d25 100644 --- a/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/MicroProfileConfig.java +++ b/server/implementation-cdi/src/main/java/io/smallrye/graphql/cdi/config/MicroProfileConfig.java @@ -39,6 +39,8 @@ public class MicroProfileConfig implements Config { private Optional parserCaptureSourceLocation; private Optional parserCaptureLineComments; private Optional parserCaptureIgnoredChars; + private Optional queryComplexityInstrumentation; + private Optional queryDepthInstrumentation; @Override public String getName() { @@ -248,6 +250,26 @@ public Optional> getErrorExtensionFields() { return errorExtensionFields; } + @Override + public Optional getQueryComplexityInstrumentation() { + if (queryComplexityInstrumentation == null) { + org.eclipse.microprofile.config.Config microProfileConfig = ConfigProvider.getConfig(); + queryComplexityInstrumentation = microProfileConfig.getOptionalValue(ConfigKey.INSTRUMENTATION_QUERY_COMPLEXITY, + Integer.class); + } + return queryComplexityInstrumentation; + } + + @Override + public Optional getQueryDepthInstrumentation() { + if (queryDepthInstrumentation == null) { + org.eclipse.microprofile.config.Config microProfileConfig = ConfigProvider.getConfig(); + queryDepthInstrumentation = microProfileConfig.getOptionalValue(ConfigKey.INSTRUMENTATION_QUERY_DEPTH, + Integer.class); + } + return queryDepthInstrumentation; + } + @Override public T getConfigValue(String key, Class type, T defaultValue) { org.eclipse.microprofile.config.Config microProfileConfig = ConfigProvider.getConfig(); @@ -390,6 +412,14 @@ public void setIncludeIntrospectionTypesInSchema(Boolean includeIntrospectionTyp this.includeIntrospectionTypesInSchema = includeIntrospectionTypesInSchema; } + public void setQueryComplexityInstrumentation(Optional queryComplexityInstrumentation) { + this.queryComplexityInstrumentation = queryComplexityInstrumentation; + } + + public void getQueryDepthInstrumentation(Optional queryDepthInstrumentation) { + this.queryDepthInstrumentation = queryDepthInstrumentation; + } + private Optional> mergeList(Optional> currentList, Optional> deprecatedList) { List combined = new ArrayList<>(); diff --git a/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java b/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java index 63e8124c7..b18ae1f6b 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java @@ -20,6 +20,8 @@ import graphql.ExecutionInput.Builder; import graphql.ExecutionResult; import graphql.GraphQL; +import graphql.analysis.MaxQueryComplexityInstrumentation; +import graphql.analysis.MaxQueryDepthInstrumentation; import graphql.execution.ExecutionId; import graphql.execution.ExecutionStrategy; import graphql.execution.SubscriptionExecutionStrategy; @@ -141,8 +143,6 @@ public void execute(JsonObject jsonInput, Map context, Execution GraphQL g = getGraphQL(); if (g != null) { - setParserOptions(); - // Query Builder executionBuilder = ExecutionInput.newExecutionInput() .query(query) @@ -248,9 +248,21 @@ private DataLoaderRegistry getDataLoaderRegistry(List operatio private GraphQL getGraphQL() { if (this.graphQL == null) { if (graphQLSchema != null) { + Config config = Config.get(); + setParserOptions(config); + GraphQL.Builder graphqlBuilder = GraphQL.newGraphQL(graphQLSchema); graphqlBuilder = graphqlBuilder.defaultDataFetcherExceptionHandler(new ExceptionHandler()); + if (config.getQueryComplexityInstrumentation().isPresent()) { + graphqlBuilder = graphqlBuilder.instrumentation( + new MaxQueryComplexityInstrumentation(config.getQueryComplexityInstrumentation().get())); + } + if (config.getQueryDepthInstrumentation().isPresent()) { + graphqlBuilder = graphqlBuilder + .instrumentation(new MaxQueryDepthInstrumentation(config.getQueryDepthInstrumentation().get())); + } graphqlBuilder = graphqlBuilder.instrumentation(queryCache); + graphqlBuilder = graphqlBuilder.preparsedDocumentProvider(queryCache); if (queryExecutionStrategy != null) { @@ -277,8 +289,7 @@ private GraphQL getGraphQL() { } - private void setParserOptions() { - Config config = Config.get(); + private void setParserOptions(Config config) { if (config.hasParserOptions()) { ParserOptions.Builder parserOptionsBuilder = ParserOptions.newParserOptions(); if (config.isParserCaptureIgnoredChars().isPresent()) { diff --git a/server/implementation/src/main/java/io/smallrye/graphql/spi/config/Config.java b/server/implementation/src/main/java/io/smallrye/graphql/spi/config/Config.java index 6576a6a17..c52d4e391 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/spi/config/Config.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/spi/config/Config.java @@ -191,6 +191,14 @@ default boolean hasParserOptions() { || getParserMaxWhitespaceTokens().isPresent(); } + default Optional getQueryComplexityInstrumentation() { + return Optional.empty(); + } + + default Optional getQueryDepthInstrumentation() { + return Optional.empty(); + } + default String getFieldVisibility() { return FIELD_VISIBILITY_DEFAULT; } From b8365917dee706b684bc793fce50eb8f7e67218f Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Tue, 9 Aug 2022 15:22:58 +1000 Subject: [PATCH 6/6] Chain the instrumentation Signed-off-by: Phillip Kruger --- .../graphql/execution/ExecutionService.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java b/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java index b18ae1f6b..a270f4fec 100644 --- a/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java +++ b/server/implementation/src/main/java/io/smallrye/graphql/execution/ExecutionService.java @@ -2,6 +2,7 @@ import static io.smallrye.graphql.SmallRyeGraphQLServerLogging.log; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -25,6 +26,8 @@ import graphql.execution.ExecutionId; import graphql.execution.ExecutionStrategy; import graphql.execution.SubscriptionExecutionStrategy; +import graphql.execution.instrumentation.ChainedInstrumentation; +import graphql.execution.instrumentation.Instrumentation; import graphql.parser.ParserOptions; import graphql.schema.GraphQLSchema; import io.smallrye.graphql.bootstrap.DataFetcherFactory; @@ -253,15 +256,18 @@ private GraphQL getGraphQL() { GraphQL.Builder graphqlBuilder = GraphQL.newGraphQL(graphQLSchema); graphqlBuilder = graphqlBuilder.defaultDataFetcherExceptionHandler(new ExceptionHandler()); + + List chainedList = new ArrayList<>(); + if (config.getQueryComplexityInstrumentation().isPresent()) { - graphqlBuilder = graphqlBuilder.instrumentation( - new MaxQueryComplexityInstrumentation(config.getQueryComplexityInstrumentation().get())); + chainedList.add(new MaxQueryComplexityInstrumentation(config.getQueryComplexityInstrumentation().get())); } if (config.getQueryDepthInstrumentation().isPresent()) { - graphqlBuilder = graphqlBuilder - .instrumentation(new MaxQueryDepthInstrumentation(config.getQueryDepthInstrumentation().get())); + chainedList.add(new MaxQueryDepthInstrumentation(config.getQueryDepthInstrumentation().get())); } - graphqlBuilder = graphqlBuilder.instrumentation(queryCache); + chainedList.add(queryCache); + // TODO: Allow users to add custome instumentations + graphqlBuilder = graphqlBuilder.instrumentation(new ChainedInstrumentation(chainedList)); graphqlBuilder = graphqlBuilder.preparsedDocumentProvider(queryCache);