-
Notifications
You must be signed in to change notification settings - Fork 149
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1565 from newrelic/pr-1454
Pr 1454
- Loading branch information
Showing
45 changed files
with
1,479 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
dependencies { | ||
implementation(project(":agent-bridge")) | ||
|
||
implementation 'com.graphql-java:graphql-java:21.0' | ||
|
||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2' | ||
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.2' | ||
testImplementation 'org.mockito:mockito-core:4.6.1' | ||
testImplementation 'org.mockito:mockito-junit-jupiter:4.6.1' | ||
|
||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2' | ||
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.7.2'} | ||
|
||
jar { | ||
manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.graphql-java-21.0' } | ||
} | ||
|
||
java { | ||
toolchain { | ||
languageVersion.set(JavaLanguageVersion.of(11)) | ||
} | ||
} | ||
|
||
verifyInstrumentation { | ||
passesOnly 'com.graphql-java:graphql-java:[21.0,)' | ||
excludeRegex 'com.graphql-java:graphql-java:(0.0.0|201|202).*' | ||
excludeRegex 'com.graphql-java:graphql-java:.*(vTEST|-beta|-alpha1|-nf-execution|-rc|-TEST).*' | ||
} | ||
|
||
site { | ||
title 'GraphQL Java' | ||
type 'Framework' | ||
} | ||
|
||
test { | ||
useJUnitPlatform() | ||
// These instrumentation tests only run on Java 11+ regardless of the -PtestN gradle property that is set. | ||
onlyIf { | ||
!project.hasProperty('test8') | ||
} | ||
|
||
} | ||
|
56 changes: 56 additions & 0 deletions
56
...n/graphql-java-21.0/src/main/java/com/nr/instrumentation/graphql/GraphQLErrorHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* | ||
* * Copyright 2023 New Relic Corporation. All rights reserved. | ||
* * SPDX-License-Identifier: Apache-2.0 | ||
* | ||
*/ | ||
|
||
package com.nr.instrumentation.graphql; | ||
|
||
import com.newrelic.api.agent.NewRelic; | ||
import graphql.ExecutionResult; | ||
import graphql.GraphQLError; | ||
import graphql.GraphQLException; | ||
import graphql.GraphqlErrorException; | ||
import graphql.execution.FieldValueInfo; | ||
|
||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.ExecutionException; | ||
import java.util.logging.Level; | ||
|
||
public class GraphQLErrorHandler { | ||
public static void reportNonNullableExceptionToNR(FieldValueInfo result) { | ||
CompletableFuture<ExecutionResult> exceptionResult = result.getFieldValue(); | ||
if (resultHasException(exceptionResult)) { | ||
reportExceptionFromCompletedExceptionally(exceptionResult); | ||
} | ||
} | ||
|
||
public static void reportGraphQLException(GraphQLException exception) { | ||
NewRelic.noticeError(exception); | ||
} | ||
|
||
public static void reportGraphQLError(GraphQLError error) { | ||
NewRelic.noticeError(throwableFromGraphQLError(error)); | ||
} | ||
|
||
private static boolean resultHasException(CompletableFuture<ExecutionResult> exceptionResult) { | ||
return exceptionResult != null && exceptionResult.isCompletedExceptionally(); | ||
} | ||
|
||
private static void reportExceptionFromCompletedExceptionally(CompletableFuture<ExecutionResult> exceptionResult) { | ||
try { | ||
exceptionResult.get(); | ||
} catch (InterruptedException e) { | ||
NewRelic.getAgent().getLogger().log(Level.FINEST, "Could not report GraphQL exception."); | ||
} catch (ExecutionException e) { | ||
NewRelic.noticeError(e.getCause()); | ||
} | ||
} | ||
|
||
private static Throwable throwableFromGraphQLError(GraphQLError error) { | ||
return GraphqlErrorException.newErrorException() | ||
.message(error.getMessage()) | ||
.build(); | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
...ion/graphql-java-21.0/src/main/java/com/nr/instrumentation/graphql/GraphQLObfuscator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* | ||
* | ||
* * Copyright 2023 New Relic Corporation. All rights reserved. | ||
* * SPDX-License-Identifier: Apache-2.0 | ||
* | ||
*/ | ||
|
||
package com.nr.instrumentation.graphql; | ||
|
||
import graphql.com.google.common.base.Joiner; | ||
|
||
import java.util.regex.Pattern; | ||
|
||
public class GraphQLObfuscator { | ||
private static final String SINGLE_QUOTE = "'(?:[^']|'')*?(?:\\\\'.*|'(?!'))"; | ||
private static final String DOUBLE_QUOTE = "\"(?:[^\"]|\"\")*?(?:\\\\\".*|\"(?!\"))"; | ||
private static final String COMMENT = "(?:#|--).*?(?=\\r|\\n|$)"; | ||
private static final String MULTILINE_COMMENT = "/\\*(?:[^/]|/[^*])*?(?:\\*/|/\\*.*)"; | ||
private static final String UUID = "\\{?(?:[0-9a-f]\\-*){32}\\}?"; | ||
private static final String HEX = "0x[0-9a-f]+"; | ||
private static final String BOOLEAN = "\\b(?:true|false|null)\\b"; | ||
private static final String NUMBER = "-?\\b(?:[0-9]+\\.)?[0-9]+([eE][+-]?[0-9]+)?"; | ||
|
||
private static final Pattern ALL_DIALECTS_PATTERN; | ||
private static final Pattern ALL_UNMATCHED_PATTERN; | ||
|
||
static { | ||
String allDialectsPattern = Joiner.on("|").join(SINGLE_QUOTE, DOUBLE_QUOTE, UUID, HEX, | ||
MULTILINE_COMMENT, COMMENT, NUMBER, BOOLEAN); | ||
|
||
ALL_DIALECTS_PATTERN = Pattern.compile(allDialectsPattern, Pattern.DOTALL | Pattern.CASE_INSENSITIVE); | ||
ALL_UNMATCHED_PATTERN = Pattern.compile("'|\"|/\\*|\\*/|\\$", Pattern.DOTALL | Pattern.CASE_INSENSITIVE); | ||
} | ||
|
||
public static String obfuscate(final String query) { | ||
if (query == null || query.length() == 0) { | ||
return query; | ||
} | ||
String obfuscatedQuery = ALL_DIALECTS_PATTERN.matcher(query).replaceAll("***"); | ||
return checkForUnmatchedPairs(obfuscatedQuery); | ||
} | ||
|
||
private static String checkForUnmatchedPairs(final String obfuscatedQuery) { | ||
return GraphQLObfuscator.ALL_UNMATCHED_PATTERN.matcher(obfuscatedQuery).find() ? "***" : obfuscatedQuery; | ||
} | ||
} | ||
|
||
|
34 changes: 34 additions & 0 deletions
34
...ql-java-21.0/src/main/java/com/nr/instrumentation/graphql/GraphQLOperationDefinition.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* | ||
* | ||
* * Copyright 2023 New Relic Corporation. All rights reserved. | ||
* * SPDX-License-Identifier: Apache-2.0 | ||
* | ||
*/ | ||
|
||
package com.nr.instrumentation.graphql; | ||
|
||
import graphql.language.Document; | ||
import graphql.language.OperationDefinition; | ||
|
||
import java.util.List; | ||
|
||
public class GraphQLOperationDefinition { | ||
private final static String DEFAULT_OPERATION_DEFINITION_NAME = "<anonymous>"; | ||
private final static String DEFAULT_OPERATION_NAME = ""; | ||
|
||
// Multiple operations are supported for transaction name only | ||
// The underlying library does not seem to support multiple operations at time of this instrumentation | ||
public static OperationDefinition firstFrom(final Document document) { | ||
List<OperationDefinition> operationDefinitions = document.getDefinitionsOfType(OperationDefinition.class); | ||
return operationDefinitions.isEmpty() ? null : operationDefinitions.get(0); | ||
} | ||
|
||
public static String getOperationNameFrom(final OperationDefinition operationDefinition) { | ||
return operationDefinition.getName() != null ? operationDefinition.getName() : DEFAULT_OPERATION_DEFINITION_NAME; | ||
} | ||
|
||
public static String getOperationTypeFrom(final OperationDefinition operationDefinition) { | ||
OperationDefinition.Operation operation = operationDefinition.getOperation(); | ||
return operation != null ? operation.name() : DEFAULT_OPERATION_NAME; | ||
} | ||
} |
67 changes: 67 additions & 0 deletions
67
...ation/graphql-java-21.0/src/main/java/com/nr/instrumentation/graphql/GraphQLSpanUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* | ||
* | ||
* * Copyright 2023 New Relic Corporation. All rights reserved. | ||
* * SPDX-License-Identifier: Apache-2.0 | ||
* | ||
*/ | ||
|
||
package com.nr.instrumentation.graphql; | ||
|
||
import com.newrelic.agent.bridge.AgentBridge; | ||
import graphql.execution.ExecutionStrategyParameters; | ||
import graphql.language.Document; | ||
import graphql.language.OperationDefinition; | ||
import graphql.schema.GraphQLNamedSchemaElement; | ||
import graphql.schema.GraphQLOutputType; | ||
|
||
import static com.nr.instrumentation.graphql.GraphQLObfuscator.obfuscate; | ||
import static com.nr.instrumentation.graphql.GraphQLOperationDefinition.getOperationTypeFrom; | ||
import static com.nr.instrumentation.graphql.Utils.getValueOrDefault; | ||
|
||
public class GraphQLSpanUtil { | ||
|
||
private final static String DEFAULT_OPERATION_TYPE = "Unavailable"; | ||
private final static String DEFAULT_OPERATION_NAME = "<anonymous>"; | ||
|
||
public static void setOperationAttributes(final Document document, final String query) { | ||
String nonNullQuery = getValueOrDefault(query, ""); | ||
if (document == null) { | ||
setDefaultOperationAttributes(nonNullQuery); | ||
return; | ||
} | ||
OperationDefinition definition = GraphQLOperationDefinition.firstFrom(document); | ||
if (definition == null) { | ||
setDefaultOperationAttributes(nonNullQuery); | ||
} else { | ||
setOperationAttributes(getOperationTypeFrom(definition), definition.getName(), nonNullQuery); | ||
} | ||
} | ||
|
||
public static void setResolverAttributes(ExecutionStrategyParameters parameters) { | ||
AgentBridge.privateApi.addTracerParameter("graphql.field.path", parameters.getPath().getSegmentName()); | ||
AgentBridge.privateApi.addTracerParameter("graphql.field.name", parameters.getField().getName()); | ||
// ExecutionStepInfo is not nullable according to documentation | ||
GraphQLOutputType graphQLOutputType = parameters.getExecutionStepInfo().getType(); | ||
setGraphQLFieldParentTypeIfPossible(graphQLOutputType); | ||
} | ||
|
||
private static void setGraphQLFieldParentTypeIfPossible(GraphQLOutputType graphQLOutputType) { | ||
// graphql.field.parentType is NOT required according to the Agent Spec | ||
if (graphQLOutputType instanceof GraphQLNamedSchemaElement) { | ||
GraphQLNamedSchemaElement named = (GraphQLNamedSchemaElement) graphQLOutputType; | ||
AgentBridge.privateApi.addTracerParameter("graphql.field.parentType", named.getName()); | ||
} | ||
} | ||
|
||
private static void setOperationAttributes(String type, String name, String query) { | ||
AgentBridge.privateApi.addTracerParameter("graphql.operation.type", getValueOrDefault(type, DEFAULT_OPERATION_TYPE)); | ||
AgentBridge.privateApi.addTracerParameter("graphql.operation.name", getValueOrDefault(name, DEFAULT_OPERATION_NAME)); | ||
AgentBridge.privateApi.addTracerParameter("graphql.operation.query", obfuscate(query)); | ||
} | ||
|
||
private static void setDefaultOperationAttributes(String query) { | ||
AgentBridge.privateApi.addTracerParameter("graphql.operation.type", DEFAULT_OPERATION_TYPE); | ||
AgentBridge.privateApi.addTracerParameter("graphql.operation.name", DEFAULT_OPERATION_NAME); | ||
AgentBridge.privateApi.addTracerParameter("graphql.operation.query", obfuscate(query)); | ||
} | ||
} |
Oops, something went wrong.