Skip to content

Commit d39259f

Browse files
authored
Merge pull request #3276 from OpenFeign/feature/graphql-apt-alias-field-names
Add useAliasForFieldNames to graphql-apt for alias-aware naming
2 parents 668a796 + 81216a7 commit d39259f

6 files changed

Lines changed: 187 additions & 12 deletions

File tree

graphql-apt/src/main/java/feign/graphql/apt/GraphqlSchemaProcessor.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ private TypeAnnotationConfig resolveClassConfig(
418418
fqns,
419419
rawAnnotations,
420420
annotation.useOptional(),
421+
annotation.useAliasForFieldNames(),
421422
classFieldAnnotations,
422423
nonNullFqns,
423424
nonNullRaw);
@@ -434,6 +435,7 @@ private TypeAnnotationConfig resolveClassConfig(
434435
mergedImports,
435436
config.annotations(),
436437
config.useOptional(),
438+
config.useAliasForFieldNames(),
437439
config.fieldAnnotations(),
438440
config.nonNullAnnotations());
439441
}
@@ -451,10 +453,17 @@ private TypeAnnotationConfig resolveMethodConfig(
451453
ExecutableElement method, GraphqlQuery annotation, TypeAnnotationConfig classConfig) {
452454
var methodFqns = extractClassFqns(annotation::typeAnnotations);
453455
var methodRaw = annotation.rawTypeAnnotations();
454-
var methodToggle = annotation.useOptional();
456+
var methodOptionalToggle = annotation.useOptional();
457+
var methodAliasToggle = annotation.useAliasForFieldNames();
455458

456459
var useOptional =
457-
methodToggle == Toggle.INHERIT ? classConfig.useOptional() : methodToggle == Toggle.TRUE;
460+
methodOptionalToggle == Toggle.INHERIT
461+
? classConfig.useOptional()
462+
: methodOptionalToggle == Toggle.TRUE;
463+
var useAliasForFieldNames =
464+
methodAliasToggle == Toggle.INHERIT
465+
? classConfig.useAliasForFieldNames()
466+
: methodAliasToggle == Toggle.TRUE;
458467

459468
var methodFieldAnnotations = extractFieldAnnotations(method);
460469
var fieldAnnotations =
@@ -469,6 +478,7 @@ private TypeAnnotationConfig resolveMethodConfig(
469478
boolean hasMethodAnnotations = !methodFqns.isEmpty() || methodRaw.length > 0;
470479
if (!hasMethodAnnotations && !hasMethodNonNull) {
471480
if (useOptional == classConfig.useOptional()
481+
&& useAliasForFieldNames == classConfig.useAliasForFieldNames()
472482
&& fieldAnnotations.equals(classConfig.fieldAnnotations())) {
473483
return classConfig;
474484
}
@@ -480,6 +490,7 @@ private TypeAnnotationConfig resolveMethodConfig(
480490
mergedImports,
481491
classConfig.annotations(),
482492
useOptional,
493+
useAliasForFieldNames,
483494
fieldAnnotations,
484495
classConfig.nonNullAnnotations());
485496
}
@@ -488,13 +499,24 @@ private TypeAnnotationConfig resolveMethodConfig(
488499
var nonNullRaw = hasMethodNonNull ? methodNonNullRaw : new String[0];
489500
var config =
490501
TypeAnnotationConfig.resolve(
491-
methodFqns, methodRaw, useOptional, fieldAnnotations, nonNullFqns, nonNullRaw);
502+
methodFqns,
503+
methodRaw,
504+
useOptional,
505+
useAliasForFieldNames,
506+
fieldAnnotations,
507+
nonNullFqns,
508+
nonNullRaw);
492509

493510
if (resolvedNonNull != null && !resolvedNonNull.isEmpty()) {
494511
var mergedImports = new TreeSet<>(config.imports());
495512
mergedImports.addAll(classConfig.imports());
496513
return new TypeAnnotationConfig(
497-
mergedImports, config.annotations(), useOptional, fieldAnnotations, resolvedNonNull);
514+
mergedImports,
515+
config.annotations(),
516+
useOptional,
517+
useAliasForFieldNames,
518+
fieldAnnotations,
519+
resolvedNonNull);
498520
}
499521

500522
return config;

graphql-apt/src/main/java/feign/graphql/apt/TypeAnnotationConfig.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,39 @@ record TypeAnnotationConfig(
2727
Set<String> imports,
2828
List<String> annotations,
2929
boolean useOptional,
30+
boolean useAliasForFieldNames,
3031
Map<String, FieldAnnotations> fieldAnnotations,
3132
List<String> nonNullAnnotations) {
3233

3334
static final TypeAnnotationConfig EMPTY =
34-
new TypeAnnotationConfig(Set.of(), List.of(), false, Map.of(), List.of());
35+
new TypeAnnotationConfig(Set.of(), List.of(), false, true, Map.of(), List.of());
3536

3637
static TypeAnnotationConfig resolve(
37-
List<String> typeAnnotationFqns, String[] rawTypeAnnotations, boolean useOptional) {
38+
List<String> typeAnnotationFqns,
39+
String[] rawTypeAnnotations,
40+
boolean useOptional,
41+
boolean useAliasForFieldNames) {
3842
return resolve(
39-
typeAnnotationFqns, rawTypeAnnotations, useOptional, Map.of(), List.of(), new String[0]);
43+
typeAnnotationFqns,
44+
rawTypeAnnotations,
45+
useOptional,
46+
useAliasForFieldNames,
47+
Map.of(),
48+
List.of(),
49+
new String[0]);
4050
}
4151

4252
static TypeAnnotationConfig resolve(
4353
List<String> typeAnnotationFqns,
4454
String[] rawTypeAnnotations,
4555
boolean useOptional,
56+
boolean useAliasForFieldNames,
4657
Map<String, FieldAnnotations> fieldAnnotations) {
4758
return resolve(
4859
typeAnnotationFqns,
4960
rawTypeAnnotations,
5061
useOptional,
62+
useAliasForFieldNames,
5163
fieldAnnotations,
5264
List.of(),
5365
new String[0]);
@@ -57,6 +69,7 @@ static TypeAnnotationConfig resolve(
5769
List<String> typeAnnotationFqns,
5870
String[] rawTypeAnnotations,
5971
boolean useOptional,
72+
boolean useAliasForFieldNames,
6073
Map<String, FieldAnnotations> fieldAnnotations,
6174
List<String> nonNullFqns,
6275
String[] nonNullRawAnnotations) {
@@ -71,7 +84,12 @@ static TypeAnnotationConfig resolve(
7184
var nonNullResolved = resolveAnnotationList(nonNullFqns, nonNullRawAnnotations, imports);
7285

7386
return new TypeAnnotationConfig(
74-
imports, annotations, useOptional, fieldAnnotations, nonNullResolved);
87+
imports,
88+
annotations,
89+
useOptional,
90+
useAliasForFieldNames,
91+
fieldAnnotations,
92+
nonNullResolved);
7593
}
7694

7795
static List<String> resolveAnnotationList(

graphql-apt/src/main/java/feign/graphql/apt/TypeGenerator.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,12 @@ private ResultTypeDefinition buildResultType(
139139
if (!(selection instanceof Field field)) {
140140
continue;
141141
}
142-
var fieldName = field.getName();
143-
var schemaDef = GraphqlTypeMapper.findFieldDefinition(parentType, fieldName);
142+
var schemaFieldName = field.getName();
143+
var schemaDef = GraphqlTypeMapper.findFieldDefinition(parentType, schemaFieldName);
144144
if (schemaDef == null) {
145145
continue;
146146
}
147+
var fieldName = responseKey(field);
147148

148149
var fieldType = schemaDef.getType();
149150
var rawTypeName = GraphqlTypeMapper.unwrapTypeName(fieldType);
@@ -243,13 +244,20 @@ private void collectAllImports(ResultTypeDefinition tree, Set<String> imports) {
243244
}
244245
}
245246

247+
private String responseKey(Field field) {
248+
if (annotationConfig.useAliasForFieldNames() && field.getAlias() != null) {
249+
return field.getAlias();
250+
}
251+
return field.getName();
252+
}
253+
246254
private String canonicalize(SelectionSet selectionSet) {
247255
var entries = new ArrayList<String>();
248256
for (var selection : selectionSet.getSelections()) {
249257
if (!(selection instanceof Field field)) {
250258
continue;
251259
}
252-
var name = field.getName();
260+
var name = responseKey(field);
253261
if (field.getSelectionSet() != null && !field.getSelectionSet().getSelections().isEmpty()) {
254262
entries.add(name + "{" + canonicalize(field.getSelectionSet()) + "}");
255263
} else {
@@ -266,7 +274,7 @@ private String describeFields(SelectionSet selectionSet) {
266274
if (!(selection instanceof Field field)) {
267275
continue;
268276
}
269-
var name = field.getName();
277+
var name = responseKey(field);
270278
if (field.getSelectionSet() != null && !field.getSelectionSet().getSelections().isEmpty()) {
271279
entries.add(name + " { " + describeFields(field.getSelectionSet()) + " }");
272280
} else {

graphql-apt/src/test/java/feign/graphql/apt/GraphqlSchemaProcessorTest.java

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,4 +1790,127 @@ interface NonNullFieldComboApi {
17901790
contents.contains("@Deprecated String id");
17911791
contents.contains("String email");
17921792
}
1793+
1794+
@Test
1795+
void useAliasForFieldNamesGeneratesAliasedInnerRecords() {
1796+
var source =
1797+
JavaFileObjects.forSourceString(
1798+
"test.AliasApi",
1799+
"""
1800+
package test;
1801+
1802+
import feign.graphql.GraphqlSchema;
1803+
import feign.graphql.GraphqlQuery;
1804+
1805+
@GraphqlSchema(value = "test-schema.graphql", useAliasForFieldNames = true)
1806+
interface AliasApi {
1807+
@GraphqlQuery(\"""
1808+
{
1809+
starship(id: "1") {
1810+
id name
1811+
specification: specs { lengthMeters classification }
1812+
currentLocation: location { planet sector }
1813+
}
1814+
}\""")
1815+
StarshipResult getStarship();
1816+
}
1817+
""");
1818+
1819+
var compilation = javac().withProcessors(new GraphqlSchemaProcessor()).compile(source);
1820+
1821+
assertThat(compilation).succeeded();
1822+
1823+
var contents =
1824+
assertThat(compilation).generatedSourceFile("test.StarshipResult").contentsAsUtf8String();
1825+
contents.contains("Optional<Specification> specification");
1826+
contents.contains("Optional<CurrentLocation> currentLocation");
1827+
contents.contains("public record Specification(");
1828+
contents.contains("public record CurrentLocation(");
1829+
}
1830+
1831+
@Test
1832+
void useAliasForFieldNamesDisabledUsesFieldName() {
1833+
var source =
1834+
JavaFileObjects.forSourceString(
1835+
"test.NoAliasApi",
1836+
"""
1837+
package test;
1838+
1839+
import feign.graphql.GraphqlSchema;
1840+
import feign.graphql.GraphqlQuery;
1841+
1842+
@GraphqlSchema(value = "test-schema.graphql", useAliasForFieldNames = false)
1843+
interface NoAliasApi {
1844+
@GraphqlQuery(\"""
1845+
{
1846+
starship(id: "1") {
1847+
id name
1848+
specification: specs { lengthMeters classification }
1849+
currentLocation: location { planet sector }
1850+
}
1851+
}\""")
1852+
StarshipResult getStarship();
1853+
}
1854+
""");
1855+
1856+
var compilation = javac().withProcessors(new GraphqlSchemaProcessor()).compile(source);
1857+
1858+
assertThat(compilation).succeeded();
1859+
1860+
var contents =
1861+
assertThat(compilation).generatedSourceFile("test.StarshipResult").contentsAsUtf8String();
1862+
contents.contains("Optional<Specs> specs");
1863+
contents.contains("Optional<Location> location");
1864+
contents.contains("public record Specs(");
1865+
contents.contains("public record Location(");
1866+
}
1867+
1868+
@Test
1869+
void useAliasForFieldNamesMethodOverridesClassLevel() {
1870+
var source =
1871+
JavaFileObjects.forSourceString(
1872+
"test.AliasOverrideApi",
1873+
"""
1874+
package test;
1875+
1876+
import feign.graphql.GraphqlSchema;
1877+
import feign.graphql.GraphqlQuery;
1878+
import feign.graphql.Toggle;
1879+
1880+
@GraphqlSchema(value = "test-schema.graphql", useAliasForFieldNames = false)
1881+
interface AliasOverrideApi {
1882+
@GraphqlQuery(value = \"""
1883+
{
1884+
starship(id: "1") {
1885+
id name
1886+
specification: specs { lengthMeters classification }
1887+
}
1888+
}\""", useAliasForFieldNames = Toggle.TRUE)
1889+
AliasedResult getAliased();
1890+
1891+
@GraphqlQuery(\"""
1892+
{
1893+
starship(id: "1") {
1894+
id name
1895+
specification: specs { lengthMeters classification }
1896+
}
1897+
}\""")
1898+
PlainResult getPlain();
1899+
}
1900+
""");
1901+
1902+
var compilation = javac().withProcessors(new GraphqlSchemaProcessor()).compile(source);
1903+
1904+
assertThat(compilation).succeeded();
1905+
1906+
var aliased =
1907+
assertThat(compilation).generatedSourceFile("test.AliasedResult").contentsAsUtf8String();
1908+
aliased.contains("Optional<Specification> specification");
1909+
aliased.contains("public record Specification(");
1910+
1911+
var plain =
1912+
assertThat(compilation).generatedSourceFile("test.PlainResult").contentsAsUtf8String();
1913+
plain.contains("Optional<Specs> specs");
1914+
plain.contains("public record Specs(");
1915+
}
17931916
}

graphql/src/main/java/feign/graphql/GraphqlQuery.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
Toggle useOptional() default Toggle.INHERIT;
3232

33+
Toggle useAliasForFieldNames() default Toggle.INHERIT;
34+
3335
Class<?>[] typeAnnotations() default {};
3436

3537
String[] rawTypeAnnotations() default {};

graphql/src/main/java/feign/graphql/GraphqlSchema.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232

3333
boolean useOptional() default true;
3434

35+
boolean useAliasForFieldNames() default true;
36+
3537
Class<?>[] uses() default {};
3638

3739
Class<?>[] typeAnnotations() default {};

0 commit comments

Comments
 (0)