Skip to content

Commit b937c05

Browse files
committed
Support direct printing of schema view elements
1 parent 4a267e4 commit b937c05

22 files changed

Lines changed: 542 additions & 74 deletions

src/main/java/graphql/schema/GraphQLTypeReference.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import graphql.util.TraversalControl;
77
import graphql.util.TraverserContext;
88

9+
import java.util.Collections;
10+
import java.util.List;
11+
912
import static graphql.Assert.assertValidName;
1013

1114
/**
@@ -45,6 +48,11 @@ public String getDescription() {
4548
return null;
4649
}
4750

51+
@Override
52+
public List<GraphQLAppliedDirective> getAppliedDirectives() {
53+
return Collections.emptyList();
54+
}
55+
4856
@Override
4957
public Node getDefinition() {
5058
return null;

src/main/java/graphql/schema/SchemaDirectiveContainer.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,19 @@
33
import graphql.ExperimentalApi;
44
import org.jspecify.annotations.NullMarked;
55

6+
import java.util.List;
7+
68
/**
79
* A schema element on which directives can be applied.
810
*/
911
@ExperimentalApi
1012
@NullMarked
1113
public interface SchemaDirectiveContainer extends SchemaNamedElement {
14+
15+
/**
16+
* Returns the directives applied directly to this element.
17+
*
18+
* @return applied directives in declaration order
19+
*/
20+
List<? extends SchemaAppliedDirective> getAppliedDirectives();
1221
}

src/main/java/graphql/schema/SchemaFieldsContainer.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,19 @@
33
import graphql.ExperimentalApi;
44
import org.jspecify.annotations.NullMarked;
55

6+
import java.util.List;
7+
68
/**
79
* A composite type that declares output fields.
810
*/
911
@ExperimentalApi
1012
@NullMarked
1113
public interface SchemaFieldsContainer extends SchemaComposite {
14+
15+
/**
16+
* Returns all fields declared by this type without applying schema-level visibility.
17+
*
18+
* @return the declared field definitions
19+
*/
20+
List<? extends SchemaField> getFieldDefinitions();
1221
}

src/main/java/graphql/schema/SchemaInputFieldsContainer.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,19 @@
33
import graphql.ExperimentalApi;
44
import org.jspecify.annotations.NullMarked;
55

6+
import java.util.List;
7+
68
/**
79
* A named type that declares input fields.
810
*/
911
@ExperimentalApi
1012
@NullMarked
1113
public interface SchemaInputFieldsContainer extends SchemaNamedType {
14+
15+
/**
16+
* Returns all input fields declared by this type without applying schema-level visibility.
17+
*
18+
* @return the declared input field definitions
19+
*/
20+
List<? extends SchemaInputField> getFieldDefinitions();
1221
}

src/main/java/graphql/schema/SchemaScalar.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
public interface SchemaScalar
1313
extends SchemaNamedType, SchemaInputType, SchemaOutputType {
1414

15+
/**
16+
* Returns the coercing associated with this scalar.
17+
*
18+
* @return the scalar coercing
19+
*/
20+
Coercing<?, ?> getCoercing();
21+
1522
/**
1623
* Returns the URL identifying the specification implemented by this scalar.
1724
*

src/main/java/graphql/schema/idl/SchemaPrinter.java

Lines changed: 92 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import graphql.language.TypeDefinition;
3434
import graphql.language.UnionTypeDefinition;
3535
import graphql.language.Value;
36+
import graphql.schema.Coercing;
3637
import graphql.schema.DefaultGraphqlTypeComparatorRegistry;
3738
import graphql.schema.ExecutableSchema;
3839
import graphql.schema.GraphQLAppliedDirective;
@@ -1486,9 +1487,29 @@ private String directiveDefinition(
14861487
}
14871488

14881489
public String print(GraphQLType type) {
1490+
return print((SchemaType) type);
1491+
}
1492+
1493+
/**
1494+
* Prints a schema type without applying schema-level visibility.
1495+
*
1496+
* @param type the standalone schema type
1497+
*
1498+
* @return the logical type definition
1499+
*/
1500+
@ExperimentalApi
1501+
public String print(SchemaType type) {
14891502
StringWriter sw = new StringWriter();
14901503
PrintWriter out = new PrintWriter(sw);
14911504

1505+
printType(out, type);
1506+
1507+
return trimNewLineChars(sw.toString());
1508+
}
1509+
1510+
private void printType(
1511+
PrintWriter out,
1512+
SchemaType type) {
14921513
if (type instanceof SchemaNamedElement) {
14931514
printSchemaElement(
14941515
out,
@@ -1497,22 +1518,25 @@ public String print(GraphQLType type) {
14971518
} else {
14981519
out.print("Type not implemented : " + type + "\n");
14991520
}
1500-
1501-
return trimNewLineChars(sw.toString());
15021521
}
15031522

1504-
public String print(List<GraphQLSchemaElement> elements) {
1523+
/**
1524+
* Prints standalone schema types and directive definitions without applying schema-level
1525+
* visibility.
1526+
*
1527+
* @param elements the elements to print
1528+
*
1529+
* @return the logical schema definitions
1530+
*/
1531+
public String print(List<? extends SchemaElement> elements) {
15051532
StringWriter sw = new StringWriter();
15061533
PrintWriter out = new PrintWriter(sw);
15071534

1508-
for (GraphQLSchemaElement element : elements) {
1509-
if (element instanceof GraphQLDirective) {
1510-
out.print(print(((GraphQLDirective) element)));
1511-
} else if (element instanceof GraphQLType) {
1512-
printSchemaElement(
1513-
out,
1514-
(SchemaNamedElement) element,
1515-
null);
1535+
for (SchemaElement element : elements) {
1536+
if (element instanceof SchemaDirective) {
1537+
out.print(print((SchemaDirective) element));
1538+
} else if (element instanceof SchemaType) {
1539+
printType(out, (SchemaType) element);
15161540
} else {
15171541
Assert.assertShouldNeverHappen("How did we miss a %s", element.getClass());
15181542
}
@@ -1521,7 +1545,19 @@ public String print(List<GraphQLSchemaElement> elements) {
15211545
}
15221546

15231547
public String print(GraphQLDirective graphQLDirective) {
1524-
return directiveDefinition(null, graphQLDirective);
1548+
return print((SchemaDirective) graphQLDirective);
1549+
}
1550+
1551+
/**
1552+
* Prints a standalone directive definition.
1553+
*
1554+
* @param directive the directive definition
1555+
*
1556+
* @return the logical directive definition
1557+
*/
1558+
@ExperimentalApi
1559+
public String print(SchemaDirective directive) {
1560+
return directiveDefinition(null, directive);
15251561
}
15261562

15271563
private void printSchemaElement(
@@ -1710,12 +1746,16 @@ private List<? extends SchemaAppliedDirective> getAppliedDirectives(
17101746
if (schema != null) {
17111747
return schema.getAppliedDirectives(container);
17121748
}
1713-
GraphQLDirectiveContainer graphQLContainer =
1714-
(GraphQLDirectiveContainer) container;
1715-
if (isDeprecated(graphQLContainer)) {
1716-
return addOrUpdateDeprecatedDirectiveIfNeeded(graphQLContainer);
1749+
if (container instanceof GraphQLDirectiveContainer) {
1750+
GraphQLDirectiveContainer graphQLContainer =
1751+
(GraphQLDirectiveContainer) container;
1752+
if (isDeprecated(graphQLContainer)) {
1753+
return addOrUpdateDeprecatedDirectiveIfNeeded(
1754+
graphQLContainer);
1755+
}
1756+
return DirectivesUtil.toAppliedDirectives(graphQLContainer);
17171757
}
1718-
return DirectivesUtil.toAppliedDirectives(graphQLContainer);
1758+
return container.getAppliedDirectives();
17191759
}
17201760

17211761
private boolean isDeprecated(
@@ -1744,10 +1784,7 @@ private List<? extends SchemaField> getFields(
17441784
if (schema != null) {
17451785
return schema.getFields(type);
17461786
}
1747-
if (type instanceof GraphQLObjectType) {
1748-
return ((GraphQLObjectType) type).getFieldDefinitions();
1749-
}
1750-
return ((GraphQLInterfaceType) type).getFieldDefinitions();
1787+
return type.getFieldDefinitions();
17511788
}
17521789

17531790
private List<? extends SchemaInputField> getInputFields(
@@ -1756,7 +1793,7 @@ private List<? extends SchemaInputField> getInputFields(
17561793
if (schema != null) {
17571794
return schema.getInputFields(type);
17581795
}
1759-
return ((GraphQLInputObjectType) type).getFieldDefinitions();
1796+
return type.getFieldDefinitions();
17601797
}
17611798

17621799
private String printValue(
@@ -1776,14 +1813,14 @@ private String printValue(
17761813
(Value<?>) assertNotNull(value.getValue()));
17771814
}
17781815
return AstPrinter.printAst(valueToLiteral(
1779-
assertNotNull(schema),
1816+
schema,
17801817
value.getValue(),
17811818
type,
17821819
value.isInternal()));
17831820
}
17841821

17851822
private Value<?> valueToLiteral(
1786-
ExecutableSchema schema,
1823+
@Nullable ExecutableSchema schema,
17871824
@Nullable Object value,
17881825
SchemaInputType type,
17891826
boolean internal) {
@@ -1806,7 +1843,10 @@ private Value<?> valueToLiteral(
18061843
internal);
18071844
}
18081845
if (unwrappedType instanceof SchemaEnum) {
1809-
return EnumValue.newEnumValue(String.valueOf(value)).build();
1846+
return enumLiteral(
1847+
value,
1848+
(SchemaEnum) unwrappedType,
1849+
internal);
18101850
}
18111851
return scalarLiteral(
18121852
schema,
@@ -1824,7 +1864,7 @@ private SchemaInputType unwrapNonNull(SchemaInputType type) {
18241864
}
18251865

18261866
private Value<?> listLiteral(
1827-
ExecutableSchema schema,
1867+
@Nullable ExecutableSchema schema,
18281868
Object value,
18291869
SchemaList type,
18301870
boolean internal) {
@@ -1842,7 +1882,7 @@ private Value<?> listLiteral(
18421882
}
18431883

18441884
private Value<?> objectLiteral(
1845-
ExecutableSchema schema,
1885+
@Nullable ExecutableSchema schema,
18461886
Object value,
18471887
SchemaInputObject type,
18481888
boolean internal) {
@@ -1855,7 +1895,7 @@ private Value<?> objectLiteral(
18551895
}
18561896
Map<?, ?> map = (Map<?, ?>) value;
18571897
List<ObjectField> fields = new ArrayList<>();
1858-
for (SchemaInputField field : schema.getInputFields(type)) {
1898+
for (SchemaInputField field : getInputFields(schema, type)) {
18591899
if (!map.containsKey(field.getName())) {
18601900
continue;
18611901
}
@@ -1871,18 +1911,39 @@ private Value<?> objectLiteral(
18711911
return ObjectValue.newObjectValue().objectFields(fields).build();
18721912
}
18731913

1914+
private Value<?> enumLiteral(
1915+
Object value,
1916+
SchemaEnum type,
1917+
boolean internal) {
1918+
if (!internal) {
1919+
return type.valueToLiteral(
1920+
value,
1921+
GraphQLContext.getDefault(),
1922+
Locale.getDefault());
1923+
}
1924+
Object serialized = type.serialize(
1925+
value,
1926+
GraphQLContext.getDefault(),
1927+
Locale.getDefault());
1928+
return EnumValue.newEnumValue(
1929+
String.valueOf(serialized)).build();
1930+
}
1931+
18741932
private Value<?> scalarLiteral(
1875-
ExecutableSchema schema,
1933+
@Nullable ExecutableSchema schema,
18761934
Object value,
18771935
SchemaScalar type,
18781936
boolean internal) {
1937+
Coercing<?, ?> coercing = schema == null
1938+
? type.getCoercing()
1939+
: schema.getScalarCoercing(type);
18791940
Object externalValue = internal
1880-
? schema.getScalarCoercing(type).serialize(
1941+
? coercing.serialize(
18811942
value,
18821943
GraphQLContext.getDefault(),
18831944
Locale.getDefault())
18841945
: value;
1885-
return schema.getScalarCoercing(type).valueToLiteral(
1946+
return coercing.valueToLiteral(
18861947
assertNotNull(externalValue),
18871948
GraphQLContext.getDefault(),
18881949
Locale.getDefault());

src/main/java/graphql/schema/universe/SUImporter.java

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -390,8 +390,25 @@ private void addAppliedDirectives(
390390
SUAppliedDirectiveContainer containerVertex) {
391391
List<GraphQLAppliedDirective> directives =
392392
DirectivesUtil.toAppliedDirectives(container);
393-
addAppliedDirectives(directives, containerVertex);
394-
addDeprecatedDirective(container, directives, containerVertex);
393+
String reason = deprecationReason(container);
394+
boolean hasDeprecatedDirective = false;
395+
for (GraphQLAppliedDirective directive : directives) {
396+
if (Directives.DeprecatedDirective.getName().equals(
397+
directive.getName())) {
398+
hasDeprecatedDirective = true;
399+
if (replaceDeprecationReason(
400+
directive,
401+
reason,
402+
containerVertex)) {
403+
continue;
404+
}
405+
}
406+
SUAppliedDirective directiveVertex = appliedDirective(directive);
407+
builder().addAppliedDirective(containerVertex, directiveVertex);
408+
}
409+
if (reason != null && !hasDeprecatedDirective) {
410+
addDeprecatedDirective(reason, containerVertex);
411+
}
395412
}
396413

397414
private void addAppliedDirectives(
@@ -403,14 +420,27 @@ private void addAppliedDirectives(
403420
}
404421
}
405422

406-
private void addDeprecatedDirective(
407-
GraphQLDirectiveContainer container,
408-
List<GraphQLAppliedDirective> directives,
423+
private boolean replaceDeprecationReason(
424+
GraphQLAppliedDirective directive,
425+
@Nullable String reason,
409426
SUAppliedDirectiveContainer containerVertex) {
410-
String reason = deprecationReason(container);
411-
if (reason == null || hasDirective(directives, Directives.DeprecatedDirective.getName())) {
412-
return;
427+
if (reason == null) {
428+
return false;
429+
}
430+
GraphQLAppliedDirectiveArgument argument =
431+
directive.getArgument("reason");
432+
if (argument != null
433+
&& argument.getArgumentValue()
434+
== graphql.schema.InputValueWithState.NOT_SET) {
435+
return false;
413436
}
437+
addDeprecatedDirective(reason, containerVertex);
438+
return true;
439+
}
440+
441+
private void addDeprecatedDirective(
442+
String reason,
443+
SUAppliedDirectiveContainer containerVertex) {
414444
addStringDirective(
415445
Directives.DeprecatedDirective.getName(),
416446
"reason",

0 commit comments

Comments
 (0)