Last active
September 10, 2022 06:22
-
-
Save nilshartmann/d6e56b7870c34a0c69ae5de3daa51e3a to your computer and use it in GitHub Desktop.
Write usage of deprecated GraphQL fields to extensions field and dump on browser console (graphql-java/Spring for GraphQL, Apollo Client)
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
import { | |
ApolloLink, | |
} from "@apollo/client"; | |
type DeprecatedField = { | |
name: string; | |
path: string; | |
reason: string; | |
}; | |
const deprecationLink = new ApolloLink((operation, forward) => { | |
return forward(operation).map((data) => { | |
const deprecations = data.extensions?.deprecations as DeprecatedField[]; | |
if (deprecations) { | |
console.warn( | |
`In operation %c${ | |
operation.operationName || "untitled" | |
}%c you fetched deprecated fields: \n${deprecations | |
.map( | |
(d) => | |
` Field: %c${d.name}%c\n Location: %c${d.path}%c\n Deprecation reason: %c${d.reason}%c\n` | |
) | |
.join("\n")}`, | |
"font-weight: bold", | |
"font-weight: regular", | |
...deprecations.flatMap((_) => [ | |
"font-weight: bold", | |
"font-weight: regular", | |
]), | |
...deprecations.flatMap((_) => [ | |
"font-weight: bold", | |
"font-weight: regular", | |
]), | |
...deprecations.flatMap((_) => [ | |
"font-style: italic", | |
"font-style: normal", | |
]) | |
); | |
} | |
return data; | |
}); | |
}); | |
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
package nh.publy.backend.util; | |
import graphql.ExecutionResult; | |
import graphql.ExecutionResultImpl; | |
import graphql.execution.instrumentation.InstrumentationContext; | |
import graphql.execution.instrumentation.InstrumentationState; | |
import graphql.execution.instrumentation.SimpleInstrumentation; | |
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters; | |
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; | |
import graphql.schema.GraphQLFieldDefinition; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import java.util.*; | |
import java.util.concurrent.CompletableFuture; | |
import static graphql.execution.instrumentation.SimpleInstrumentationContext.noOp; | |
import static graphql.schema.GraphQLTypeUtil.simplePrint; | |
// To register the Instrumentation | |
// In Spring for GraphQL simply add the org.springframework.stereotype.Component to the class | |
// In graphql-java register it with: | |
// GraphQL.newGraphQL(schema) | |
// .instrumentation(new DeprecationInstrumentation()) | |
// .build(); | |
public class DeprecationInstrumentation extends SimpleInstrumentation { | |
private static final Logger log = LoggerFactory.getLogger(DeprecationInstrumentation.class); | |
private record DeprecatedField(String name, String path, String reason) { | |
} | |
static class DeprecationState implements InstrumentationState { | |
private final List<DeprecatedField> deprecatedFields = new LinkedList<>(); | |
public Optional<List<DeprecatedField>> snapshot() { | |
return deprecatedFields.isEmpty() ? Optional.empty() : Optional.of(deprecatedFields); | |
} | |
public void addDeprecatedField(String name, String path, String reason) { | |
if (this.deprecatedFields.stream(). | |
noneMatch(f -> f.path.equals(path))) { | |
this.deprecatedFields.add(new DeprecatedField(name, path, reason)); | |
} | |
} | |
} | |
@Override | |
public InstrumentationState createState() { | |
return new DeprecationState(); | |
} | |
@Override | |
public CompletableFuture<ExecutionResult> instrumentExecutionResult(ExecutionResult executionResult, InstrumentationExecutionParameters parameters) { | |
DeprecationState deprecationState = parameters.getInstrumentationState(); | |
ExecutionResult result = deprecationState.snapshot() | |
.map(snapshot -> addSnapshotToResult(executionResult, snapshot)) | |
.orElse(executionResult); | |
return CompletableFuture.completedFuture(result); | |
} | |
private ExecutionResult addSnapshotToResult(ExecutionResult executionResult, | |
List<DeprecationInstrumentation.DeprecatedField> snapshot) { | |
var currentExt = executionResult.getExtensions(); | |
var newExt = new LinkedHashMap<>(currentExt == null ? Collections.emptyMap() : currentExt); | |
newExt.put("deprecations", snapshot); | |
return new ExecutionResultImpl( | |
executionResult.getData(), | |
executionResult.getErrors(), | |
newExt | |
); | |
} | |
@Override | |
public InstrumentationContext<Object> beginFieldFetch(InstrumentationFieldFetchParameters parameters) { | |
GraphQLFieldDefinition field = parameters.getField(); | |
if (field.isDeprecated()) { | |
DeprecationState deprecationState = parameters.getInstrumentationState(); | |
var executionStepInfo = parameters.getEnvironment().getExecutionStepInfo(); | |
var path = String.join("/", executionStepInfo.getPath().getKeysOnly()); | |
var reason = field.getDeprecationReason(); | |
var fieldName = simplePrint(executionStepInfo.getParent().getUnwrappedNonNullType()) + "." + executionStepInfo.getFieldDefinition().getName(); | |
deprecationState.addDeprecatedField(fieldName, path, reason); | |
} | |
return noOp(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment