graphql.schema.idl.SchemaGeneratorAppliedDirectiveHelper Maven / Gradle / Ivy
package graphql.schema.idl;
import graphql.Internal;
import graphql.introspection.Introspection;
import graphql.language.Argument;
import graphql.language.Directive;
import graphql.language.DirectiveDefinition;
import graphql.language.InputValueDefinition;
import graphql.language.StringValue;
import graphql.language.Type;
import graphql.language.Value;
import graphql.schema.GraphQLAppliedDirective;
import graphql.schema.GraphQLAppliedDirectiveArgument;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLDirective;
import graphql.schema.GraphQLInputType;
import graphql.schema.GraphqlDirectivesContainerTypeBuilder;
import graphql.schema.GraphqlTypeComparatorRegistry;
import graphql.util.FpKit;
import graphql.util.Pair;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import static graphql.Directives.NO_LONGER_SUPPORTED;
import static graphql.collect.ImmutableKit.emptyList;
import static graphql.collect.ImmutableKit.map;
import static graphql.introspection.Introspection.DirectiveLocation.ARGUMENT_DEFINITION;
import static graphql.schema.idl.SchemaGeneratorHelper.buildDescription;
import static graphql.util.Pair.pair;
import static java.util.stream.Collectors.toMap;
/**
* This contains helper code to build out appliedm directives on schema element
*/
@Internal
class SchemaGeneratorAppliedDirectiveHelper {
static void buildAppliedDirectives(SchemaGeneratorHelper.BuildContext buildCtx, GraphqlDirectivesContainerTypeBuilder, ?> builder, Pair, List> appliedDirectives) {
builder.clearDirectives();
// for legacy reasons we can use the old directives construct. This is false by default
if (!buildCtx.options.isUseAppliedDirectivesOnly()) {
for (GraphQLDirective directive : appliedDirectives.first) {
builder.withDirective(directive);
}
}
for (GraphQLAppliedDirective appliedDirective : appliedDirectives.second) {
builder.withAppliedDirective(appliedDirective);
}
}
static Pair, List> buildAppliedDirectives(
SchemaGeneratorHelper.BuildContext buildCtx,
Function, GraphQLInputType> inputTypeFactory,
List directives,
List extensionDirectives,
Introspection.DirectiveLocation directiveLocation,
Set runtimeDirectives,
GraphqlTypeComparatorRegistry comparatorRegistry) {
directives = Optional.ofNullable(directives).orElse(emptyList());
extensionDirectives = Optional.ofNullable(extensionDirectives).orElse(emptyList());
List output = new ArrayList<>();
List outputApplied = new ArrayList<>();
for (Directive directive : directives) {
Pair pair = buildAppliedDirective(buildCtx,
inputTypeFactory,
directive,
runtimeDirectives,
directiveLocation,
comparatorRegistry);
output.add(pair.first);
outputApplied.add(pair.second);
}
for (Directive directive : extensionDirectives) {
Pair pair = buildAppliedDirective(buildCtx,
inputTypeFactory,
directive,
runtimeDirectives,
directiveLocation,
comparatorRegistry);
output.add(pair.first);
outputApplied.add(pair.second);
}
return pair(output, outputApplied);
}
// builds directives from a type and its extensions
private static Pair buildAppliedDirective(SchemaGeneratorHelper.BuildContext buildCtx,
Function, GraphQLInputType> inputTypeFactory,
Directive directive,
Set directiveDefinitions,
Introspection.DirectiveLocation directiveLocation,
GraphqlTypeComparatorRegistry comparatorRegistry) {
GraphQLDirective.Builder builder = GraphQLDirective.newDirective()
.name(directive.getName())
.description(buildDescription(buildCtx, directive, null))
.comparatorRegistry(comparatorRegistry)
.validLocations(directiveLocation);
GraphQLAppliedDirective.Builder builderAppliedDirective = GraphQLAppliedDirective.newDirective()
.name(directive.getName())
.description(buildDescription(buildCtx, directive, null))
.comparatorRegistry(comparatorRegistry);
Optional directiveDefOpt = FpKit.findOne(directiveDefinitions, dd -> dd.getName().equals(directive.getName()));
GraphQLDirective graphQLDirective = directiveDefOpt.orElseGet(() -> {
return buildDirectiveDefinitionFromAst(buildCtx, buildCtx.getTypeRegistry().getDirectiveDefinition(directive.getName()).get(), inputTypeFactory);
});
builder.repeatable(graphQLDirective.isRepeatable());
builder.definition(buildCtx.isCaptureAstDefinitions() ? graphQLDirective.getDefinition() : null);
builderAppliedDirective.definition(buildCtx.isCaptureAstDefinitions() ? directive : null);
List directiveArguments = new ArrayList<>();
List appliedArguments = new ArrayList<>();
for (Argument arg : directive.getArguments()) {
directiveArguments.add(buildDirectiveArg(buildCtx, arg, graphQLDirective));
appliedArguments.add(buildAppliedArg(buildCtx, arg, graphQLDirective));
}
directiveArguments = transferMissingArguments(buildCtx, directiveArguments, graphQLDirective);
directiveArguments.forEach(builder::argument);
appliedArguments = transferMissingAppliedArguments(appliedArguments, graphQLDirective);
appliedArguments.forEach(builderAppliedDirective::argument);
return pair(builder.build(), builderAppliedDirective.build());
}
private static GraphQLArgument buildDirectiveArg(SchemaGeneratorHelper.BuildContext buildCtx, Argument arg, GraphQLDirective directiveDefinition) {
GraphQLArgument directiveDefArgument = directiveDefinition.getArgument(arg.getName());
GraphQLArgument.Builder builder = GraphQLArgument.newArgument();
GraphQLInputType inputType = directiveDefArgument.getType();
builder.name(arg.getName())
.type(inputType)
.definition(buildCtx.isCaptureAstDefinitions() ? directiveDefArgument.getDefinition() : null);
// we know it is a literal because it was created by SchemaGenerator
if (directiveDefArgument.getArgumentDefaultValue().isSet()) {
builder.defaultValueLiteral((Value>) directiveDefArgument.getArgumentDefaultValue().getValue());
}
// Object value = buildCtx, arg.getValue(), inputType);
// we put the default value in if the specified is null
if (arg.getValue() != null) {
//TODO: maybe validation of it
builder.valueLiteral(arg.getValue());
}
return builder.build();
}
private static GraphQLAppliedDirectiveArgument buildAppliedArg(SchemaGeneratorHelper.BuildContext buildCtx, Argument arg, GraphQLDirective directiveDefinition) {
GraphQLArgument directiveDefArgument = directiveDefinition.getArgument(arg.getName());
GraphQLAppliedDirectiveArgument.Builder builder = GraphQLAppliedDirectiveArgument.newArgument();
builder.name(arg.getName())
.type(directiveDefArgument.getType())
.definition(buildCtx.isCaptureAstDefinitions() ? arg : null);
// Object value = buildCtx, arg.getValue(), inputType);
// we put the default value in if the specified is null
if (arg.getValue() != null) {
builder.valueLiteral(arg.getValue());
} else {
// we know it is a literal because it was created by SchemaGenerator
if (directiveDefArgument.getArgumentDefaultValue().isSet()) {
builder.valueLiteral((Value>) directiveDefArgument.getArgumentDefaultValue().getValue());
}
}
return builder.build();
}
private static List transferMissingArguments(SchemaGeneratorHelper.BuildContext buildCtx, List arguments, GraphQLDirective directiveDefinition) {
Map declaredArgs = FpKit.getByName(arguments, GraphQLArgument::getName, FpKit.mergeFirst());
List argumentsOut = new ArrayList<>(arguments);
for (GraphQLArgument directiveDefArg : directiveDefinition.getArguments()) {
if (!declaredArgs.containsKey(directiveDefArg.getName())) {
GraphQLArgument.Builder missingArg = GraphQLArgument.newArgument()
.name(directiveDefArg.getName())
.description(directiveDefArg.getDescription())
.definition(buildCtx.isCaptureAstDefinitions() ? directiveDefArg.getDefinition() : null)
.type(directiveDefArg.getType());
if (directiveDefArg.hasSetDefaultValue()) {
missingArg.defaultValueLiteral((Value>) directiveDefArg.getArgumentDefaultValue().getValue());
}
if (directiveDefArg.hasSetValue()) {
missingArg.valueLiteral((Value>) directiveDefArg.getArgumentValue().getValue());
}
argumentsOut.add(missingArg.build());
}
}
return argumentsOut;
}
private static List transferMissingAppliedArguments(List arguments, GraphQLDirective directiveDefinition) {
Map declaredArgs = FpKit.getByName(arguments, GraphQLAppliedDirectiveArgument::getName, FpKit.mergeFirst());
List argumentsOut = new ArrayList<>(arguments);
for (GraphQLArgument directiveDefArg : directiveDefinition.getArguments()) {
if (!declaredArgs.containsKey(directiveDefArg.getName())) {
GraphQLAppliedDirectiveArgument.Builder missingArg = GraphQLAppliedDirectiveArgument.newArgument()
.name(directiveDefArg.getName())
.type(directiveDefArg.getType())
.description(directiveDefArg.getDescription());
if (directiveDefArg.hasSetDefaultValue()) {
missingArg.valueLiteral((Value>) directiveDefArg.getArgumentDefaultValue().getValue());
}
if (directiveDefArg.hasSetValue()) {
missingArg.valueLiteral((Value>) directiveDefArg.getArgumentValue().getValue());
}
argumentsOut.add(missingArg.build());
}
}
return argumentsOut;
}
static GraphQLDirective buildDirectiveDefinitionFromAst(SchemaGeneratorHelper.BuildContext buildCtx, DirectiveDefinition directiveDefinition, Function, GraphQLInputType> inputTypeFactory) {
GraphQLDirective.Builder builder = GraphQLDirective.newDirective()
.name(directiveDefinition.getName())
.definition(buildCtx.isCaptureAstDefinitions() ? directiveDefinition : null)
.repeatable(directiveDefinition.isRepeatable())
.description(buildDescription(buildCtx, directiveDefinition, directiveDefinition.getDescription()));
List locations = buildLocations(directiveDefinition);
locations.forEach(builder::validLocations);
List arguments = map(directiveDefinition.getInputValueDefinitions(),
arg -> buildDirectiveArgumentDefinitionFromAst(buildCtx, arg, inputTypeFactory));
arguments.forEach(builder::argument);
return builder.build();
}
private static List buildLocations(DirectiveDefinition directiveDefinition) {
return map(directiveDefinition.getDirectiveLocations(),
dl -> Introspection.DirectiveLocation.valueOf(dl.getName().toUpperCase()));
}
static GraphQLArgument buildDirectiveArgumentDefinitionFromAst(SchemaGeneratorHelper.BuildContext buildCtx, InputValueDefinition valueDefinition, Function, GraphQLInputType> inputTypeFactory) {
GraphQLArgument.Builder builder = GraphQLArgument.newArgument();
builder.definition(buildCtx.isCaptureAstDefinitions() ? valueDefinition : null);
builder.name(valueDefinition.getName());
builder.description(buildDescription(buildCtx, valueDefinition, valueDefinition.getDescription()));
builder.deprecate(buildDeprecationReason(valueDefinition.getDirectives()));
builder.comparatorRegistry(buildCtx.getComparatorRegistry());
GraphQLInputType inputType = inputTypeFactory.apply(valueDefinition.getType());
builder.type(inputType);
if (valueDefinition.getDefaultValue() != null) {
builder.valueLiteral(valueDefinition.getDefaultValue());
builder.defaultValueLiteral(valueDefinition.getDefaultValue());
}
Pair, List> appliedDirectives = buildAppliedDirectives(
buildCtx,
inputTypeFactory,
valueDefinition.getDirectives(),
emptyList(),
ARGUMENT_DEFINITION,
buildCtx.getDirectives(),
buildCtx.getComparatorRegistry());
buildAppliedDirectives(buildCtx, builder, appliedDirectives);
return builder.build();
}
static String buildDeprecationReason(List directives) {
directives = Optional.ofNullable(directives).orElse(emptyList());
Optional directive = directives.stream().filter(d -> "deprecated".equals(d.getName())).findFirst();
if (directive.isPresent()) {
Map args = directive.get().getArguments().stream().collect(toMap(
Argument::getName, arg -> ((StringValue) arg.getValue()).getValue()
));
if (args.isEmpty()) {
return NO_LONGER_SUPPORTED; // default value from spec
} else {
// pre flight checks have ensured its valid
return args.get("reason");
}
}
return null;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy