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
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
Expand All @@ -28,7 +29,7 @@
* @author Igor Dianov
*
*/
@Target( { TYPE, FIELD })
@Target( { TYPE, FIELD, METHOD })
@Retention(RUNTIME)
public @interface GraphQLDescription {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@

package com.introproventures.graphql.jpa.query.schema.impl;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
Expand Down Expand Up @@ -562,7 +567,68 @@ protected final boolean isValidInput(Attribute<?,?> attribute) {

private String getSchemaDescription(Member member) {
if (member instanceof AnnotatedElement) {
return getSchemaDescription((AnnotatedElement) member);
String desc = getSchemaDescription((AnnotatedElement) member);
if (desc != null) {
return(desc);
}
}

//The given Member has no @GraphQLDescription set.
//If the Member is a Method it might be a getter/setter, see if the property it represents
//is annotated with @GraphQLDescription
//Alternatively if the Member is a Field its getter might be annotated, see if its getter
//is annotated with @GraphQLDescription
if (member instanceof Method) {
Field fieldMember = getFieldByAccessor((Method)member);
if (fieldMember != null) {
return(getSchemaDescription((AnnotatedElement) fieldMember));
}
} else if (member instanceof Field) {
Method fieldGetter = getGetterOfField((Field)member);
if (fieldGetter != null) {
return(getSchemaDescription((AnnotatedElement) fieldGetter));
}
}

return null;
}

private Method getGetterOfField(Field field) {
try {
Class<?> clazz = field.getDeclaringClass();
BeanInfo info = Introspector.getBeanInfo(clazz);
PropertyDescriptor[] props = info.getPropertyDescriptors();
for (PropertyDescriptor pd : props) {
if (pd.getName().equals(field.getName())) {
return(pd.getReadMethod());
}
}
} catch (IntrospectionException e) {
e.printStackTrace();
}

return(null);
}

//from https://stackoverflow.com/questions/13192734/getting-a-property-field-name-using-getter-method-of-a-pojo-java-bean/13514566
private static Field getFieldByAccessor(Method method) {
try {
Class<?> clazz = method.getDeclaringClass();
BeanInfo info = Introspector.getBeanInfo(clazz);
PropertyDescriptor[] props = info.getPropertyDescriptors();
for (PropertyDescriptor pd : props) {
if(method.equals(pd.getWriteMethod()) || method.equals(pd.getReadMethod())) {
String fieldName = pd.getName();
try {
return(clazz.getDeclaredField(fieldName));
} catch (Throwable t) {
log.error("class '" + clazz.getName() + "' contains method '" + method.getName() + "' which is an accessor for a Field named '" + fieldName + "', error getting the field:", t);
return(null);
}
}
}
} catch (Throwable t) {
log.error("error finding Field for accessor with name '" + method.getName() + "'", t);
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import graphql.Scalars;
import graphql.schema.GraphQLInputObjectType;
import graphql.schema.GraphQLSchema;
import graphql.schema.GraphQLObjectType;

@RunWith(SpringRunner.class)
@SpringBootTest(
Expand Down Expand Up @@ -120,4 +121,66 @@ public void correctlyDerivesPageableSchemaFromGivenEntities() {

}


@Test
public void correctlyDerivesSchemaDescriptionsFromGivenEntities() {
//when
GraphQLSchema schema = builder.build();

// then
assertThat(schema)
.describedAs("Ensure the schema is generated")
.isNotNull();

//then
assertThat(schema.getQueryType().getFieldDefinition("Droid").getDescription())
.describedAs( "Ensure that Droid has the expected description")
.isEqualTo("Represents an electromechanical robot in the Star Wars Universe");

//then
assertThat(
((GraphQLObjectType)schema.getQueryType().getFieldDefinition("Droid").getType())
.getFieldDefinition("primaryFunction")
.getDescription()
)
.describedAs( "Ensure that Droid.primaryFunction has the expected description")
.isEqualTo("Documents the primary purpose this droid serves");

//then
assertThat(
((GraphQLObjectType)schema.getQueryType().getFieldDefinition("Droid").getType())
.getFieldDefinition("id")
.getDescription()
)
.describedAs( "Ensure that Droid.id has the expected description, inherited from Character")
.isEqualTo("Primary Key for the Character Class");

//then
assertThat(
((GraphQLObjectType)schema.getQueryType().getFieldDefinition("Droid").getType())
.getFieldDefinition("name")
.getDescription()
)
.describedAs( "Ensure that Droid.name has the expected description, inherited from Character")
.isEqualTo("Name of the character");

//then
assertThat(
((GraphQLObjectType)schema.getQueryType().getFieldDefinition("CodeList").getType())
.getFieldDefinition("id")
.getDescription()
)
.describedAs( "Ensure that CodeList.id has the expected description")
.isEqualTo("Primary Key for the Code List Class");

//then
assertThat(
((GraphQLObjectType)schema.getQueryType().getFieldDefinition("CodeList").getType())
.getFieldDefinition("parent")
.getDescription()
)
.describedAs( "Ensure that CodeList.parent has the expected description")
.isEqualTo("The CodeList's parent CodeList");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
@Data
public class CodeList {

@Id
@GraphQLDescription("Primary Key for the Code List Class")
Long id;

Expand All @@ -41,8 +40,18 @@ public class CodeList {
boolean active;
String description;

@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name = "parent_id")
CodeList parent;

//JPA annotations moved to getters to test that @GraphQLDescription can be placed on the field when the JPA annotation is on the getter
@Id
public Long getId() {
return(id);
}

@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name = "parent_id")
@GraphQLDescription("The CodeList's parent CodeList")
public CodeList getParent() {
return(parent);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@
@EqualsAndHashCode(callSuper=true)
public class Droid extends Character {

@GraphQLDescription("Documents the primary purpose this droid serves")
String primaryFunction;

//description moved to getter to test it gets picked up
@GraphQLDescription("Documents the primary purpose this droid serves")
public String getPrimaryFunction() {
return(primaryFunction);
}
}