8000 static record like methods are no longer supported by bbakerman · Pull Request #3251 · graphql-java/graphql-java · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions src/main/java/graphql/schema/PropertyFetchingImpl.java
< 10BC0 tr data-hunk="bf1723585192db738443d413a9aaabc72a073a7fc05ecbfd2526dd66eb0bbe2f" class="show-top-border">
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,18 @@ public Object getPropertyValue(String propertyName, Object object, GraphQLType g
//
// try by public getters name - object.getPropertyName()
try {
MethodFinder methodFinder = (rootClass, methodName) -> findPubliclyAccessibleMethod(cacheKey, rootClass, methodName, dfeInUse);
MethodFinder methodFinder = (rootClass, methodName) -> findPubliclyAccessibleMethod(cacheKey, rootClass, methodName, dfeInUse, false);
return getPropertyViaGetterMethod(object, propertyName, graphQLType, methodFinder, singleArgumentValue);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

finds non static ones first

} catch (NoSuchMethodException ignored) {
}
//
// try by public getters name - object.getPropertyName() where its static
try {
// we allow static getXXX() methods because we always have. It's strange in retrospect but
// in order to not break things we allow statics to be used. In theory this double code check is not needed
// because you CANT have a `static getFoo()` and a `getFoo()` in the same class hierarchy but to make the code read clearer
// I have repeated the lookup. Since we cache methods, this happens only once and does not slow us down
MethodFinder methodFinder = (rootClass, methodName) -> findPubliclyAccessibleMethod(cacheKey, rootClass, methodName, dfeInUse, true);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

finds static getters next

return getPropertyViaGetterMethod(object, propertyName, graphQLType, methodFinder, singleArgumentValue);
} catch (NoSuchMethodException ignored) {
}
Expand Down Expand Up @@ -215,7 +226,7 @@ private Object getPropertyViaGetterUsingPrefix(Object object, String propertyNam
* which have abstract public interfaces implemented by package-protected
* (generated) subclasses.
*/
private Method findPubliclyAccessibleMethod(CacheKey cacheKey, Class<?> rootClass, String methodName, boolean dfeInUse) throws NoSuchMethodException {
private Method findPubliclyAccessibleMethod(CacheKey cacheKey, Class<?> rootClass, String methodName, boolean dfeInUse, boolean allowStaticMethods) throws NoSuchMethodException {
Class<?> currentClass = rootClass;
while (currentClass != null) {
if (Modifier.isPublic(currentClass.getModifiers())) {
Expand All @@ -224,7 +235,7 @@ private Method findPubliclyAccessibleMethod(CacheKey cacheKey, Class<?> rootClas
// try a getter that takes singleArgumentType first (if we have one)
try {
Method method = currentClass.getMethod(methodName, singleArgumentType);
if (Modifier.isPublic(method.getModifiers())) {
if (isSuitablePublicMethod(method, allowStaticMethods)) {
METHOD_CACHE.putIfAbsent(cacheKey, new CachedMethod(method));
return method;
}
Expand All @@ -233,7 +244,7 @@ private Method findPubliclyAccessibleMethod(CacheKey cacheKey, Class<?> rootClas
}
}
Method method = currentClass.getMethod(methodName);
if (Modifier.isPublic(method.getModifiers())) {
if (isSuitablePublicMethod(method, allowStaticMethods)) {
METHOD_CACHE.putIfAbsent(cacheKey, new CachedMethod(method));
return method;
}
Expand All @@ -244,6 +255,18 @@ private Method findPubliclyAccessibleMethod(CacheKey cacheKey, Class<?> rootClas
return rootClass.getMethod(methodName);
}

private boolean isSuitablePublicMethod(Method method, boolean allowStaticMethods) {
int methodModifiers = method.getModifiers();
if (Modifier.isPublic(methodModifiers)) {
//noinspection RedundantIfStatement
if (Modifier.isStatic(methodModifiers) && !allowStaticMethods) {
return false;
}
return true;
}
return false;
}

/*
https://docs.oracle.com/en/java/javase/15/language/records.html

Expand All @@ -253,9 +276,11 @@ private Method findPubliclyAccessibleMethod(CacheKey cacheKey, Class<?> rootClas

However, we won't just restrict ourselves strictly to true records. We will find methods that are record like
and fetch them - e.g. `object.propertyName()`

We won't allow static methods for record like methods however
*/
private Method findRecordMethod(CacheKey cacheKey, Class<?> rootClass, String methodName) throws NoSuchMethodException {
return findPubliclyAccessibleMethod(cacheKey, rootClass, methodName, false);
return findPubliclyAccessibleMethod(cacheKey, rootClass, methodName, false, false);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NEVER finds static recordLike() methods now

}

private Method findViaSetAccessible(CacheKey cacheKey, Class<?> aClass, String methodName, boolean dfeInUse) throws NoSuchMethodException {
Expand Down
135 changes: 132 additions & 3 deletions src/test/groovy/graphql/schema/PropertyDataFetcherTest.groovy
4F38
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class PropertyDataFetcherTest extends Specification {
.build()
}

class SomeObject {
static class SomeObject {
String value
}

Expand Down Expand Up @@ -483,7 +483,7 @@ class PropertyDataFetcherTest extends Specification {

}

class ProductDTO {
static class ProductDTO {
String name
String model
}
Expand Down Expand Up @@ -537,7 +537,7 @@ class PropertyDataFetcherTest extends Specification {
private static class Bar implements Foo {
@Override
String getSomething() {
return "bar";
return "bar"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yellow tagged IDEA code cleanup

}
}

Expand All @@ -562,4 +562,133 @@ class PropertyDataFetcherTest extends Specification {
then:
result == "bar"
}

def "issue 3247 - record like statics should not be used"() {
given:
def payload = new UpdateOrganizerSubscriptionPayload(true, new OrganizerSubscriptionError())
PropertyDataFetcher propertyDataFetcher = new PropertyDataFetcher("success")
def dfe = Mock(DataFetchingEnvironment)
dfe.getSource() >> payload
when:
def result = propertyDataFetcher.get(dfe)

then:
result == true

// repeat - should be cached
when:
result = propertyDataFetcher.get(dfe)

then:
result == true
}

def "issue 3247 - record like statics should not be found"() {
given:
def errorShape = new OrganizerSubscriptionError()
PropertyDataFetcher propertyDataFetcher = new PropertyDataFetcher("message")
def dfe = Mock(DataFetchingEnvironment)
dfe.getSource() >> errorShape
when:
def result = propertyDataFetcher.get(dfe)

then:
result == null // not found as its a static recordLike() method

// repeat - should be cached
when:
result = propertyDataFetcher.get(dfe)

then:
result == null
}

def "issue 3247 - getter statics should be found"() {
given:
def objectInQuestion = new BarClassWithStaticProperties()
PropertyDataFetcher propertyDataFetcher = new PropertyDataFetcher("foo")
def dfe = Mock(DataFetchingEnvironment)
dfe.getSource() >> objectInQuestion
when:
def result = propertyDataFetcher.get(dfe)

then:
result == "foo"

// repeat - should be cached
when:
result = propertyDataFetcher.get(dfe)

then:
result == "foo"

when:
propertyDataFetcher = new PropertyDataFetcher("bar")
result = propertyDataFetcher.get(dfe)

then:
result == "bar"

// repeat - should be cached
when:
result = propertyDataFetcher.get(dfe)

then:
result == "bar"
}

/**
* Classes from issue to ensure we reproduce as reported by customers
*
* In the UpdateOrganizerSubscriptionPayload class we will find the getSuccess() because static recordLike() methods are no longer allowed
*/
static class OrganizerSubscriptionError {
static String message() { return "error " }
}

static class UpdateOrganizerSubscriptionPayload {
private final Boolean success< 9A70 /span>
private final OrganizerSubscriptionError error

UpdateOrganizerSubscriptionPayload(Boolean success, OrganizerSubscriptionError error) {
this.success = success
this.error = error
}

static UpdateOrganizerSubscriptionPayload success() {
// 👈 note the static factory method for creating a success payload
return new UpdateOrganizerSubscriptionPayload(Boolean.TRUE, null)
}

static UpdateOrganizerSubscriptionPayload error(OrganizerSubscriptionError error) {
// 👈 note the static factory method for creating a success payload
return new UpdateOrganizerSubscriptionPayload(null, error)
}

Boolean getSuccess() {
return success
}

OrganizerSubscriptionError getError() {
return error
}


@Override
String toString() {
return new StringJoiner(
", ", UpdateOrganizerSubscriptionPayload.class.getSimpleName() + "[", "]")
.add("success=" + success)
.add("error=" + error)
.toString()
}
}

static class FooClassWithStaticProperties {
static String getFoo() { return "foo" }
}

static class BarClassWithStaticProperties extends FooClassWithStaticProperties {
static String getBar() { return "bar" }
}
}
0