Skip to content

Commit 6bfa0b5

Browse files
Merge pull request #73 from intuit/null-check-velocity-directive
Add user defined velocity directive to replace nulls in template
2 parents 13fe683 + a484782 commit 6bfa0b5

File tree

5 files changed

+132
-66
lines changed

5 files changed

+132
-66
lines changed

pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,6 @@
153153
<version>2.12.6.1</version>
154154
</dependency>
155155

156-
<dependency>
157-
<groupId>org.apache.velocity</groupId>
158-
<artifactId>velocity-engine-core</artifactId>
159-
<version>2.3</version>
160-
</dependency>
161156
<!-- TEST DEPENDENCIES -->
162157
<dependency>
163158
<groupId>org.assertj</groupId>

src/main/java/com/intuit/graphql/orchestrator/fieldresolver/FieldResolverBatchSelectionSetSupplier.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
package com.intuit.graphql.orchestrator.fieldresolver;
22

3-
import static com.intuit.graphql.orchestrator.resolverdirective.FieldResolverDirectiveUtil.getNameFromFieldReference;
4-
import static com.intuit.graphql.orchestrator.resolverdirective.FieldResolverDirectiveUtil.ifInvalidFieldReferenceThrowException;
5-
import static com.intuit.graphql.orchestrator.resolverdirective.FieldResolverDirectiveUtil.isReferenceToFieldInParentType;
6-
import static com.intuit.graphql.orchestrator.utils.XtextTypeUtils.isPrimitiveType;
7-
import static graphql.schema.InputValueWithState.newExternalValue;
8-
93
import com.intuit.graphql.orchestrator.resolverdirective.FieldResolverDirectiveUtil;
104
import com.intuit.graphql.orchestrator.resolverdirective.ResolverArgumentDefinition;
115
import com.intuit.graphql.orchestrator.resolverdirective.ResolverDirectiveDefinition;
@@ -22,14 +16,21 @@
2216
import graphql.schema.GraphQLFieldsContainer;
2317
import graphql.schema.GraphQLType;
2418
import graphql.schema.GraphQLTypeUtil;
19+
import lombok.AllArgsConstructor;
20+
import org.apache.commons.collections4.CollectionUtils;
21+
import org.apache.commons.lang3.StringUtils;
22+
2523
import java.util.ArrayList;
2624
import java.util.List;
2725
import java.util.Map;
2826
import java.util.Objects;
2927
import java.util.function.Supplier;
30-
import lombok.AllArgsConstructor;
31-
import org.apache.commons.collections4.CollectionUtils;
32-
import org.apache.commons.lang3.StringUtils;
28+
29+
import static com.intuit.graphql.orchestrator.resolverdirective.FieldResolverDirectiveUtil.getNameFromFieldReference;
30+
import static com.intuit.graphql.orchestrator.resolverdirective.FieldResolverDirectiveUtil.ifInvalidFieldReferenceThrowException;
31+
import static com.intuit.graphql.orchestrator.resolverdirective.FieldResolverDirectiveUtil.isReferenceToFieldInParentType;
32+
import static com.intuit.graphql.orchestrator.utils.XtextTypeUtils.isPrimitiveType;
33+
import static graphql.schema.InputValueWithState.newExternalValue;
3334

3435
@AllArgsConstructor
3536
public class FieldResolverBatchSelectionSetSupplier implements Supplier<SelectionSet> {
@@ -104,7 +105,7 @@ private Value<?> resolveArgumentValue(ResolverArgumentDefinition resolverArgumen
104105

105106
} else {
106107
String typename = com.intuit.graphql.utils.XtextTypeUtils.typeName(resolverArgumentDefinition.getNamedType());
107-
String stringLiteralAstValue = compileTemplate(resolverArgumentDefinition.getValue(), parentSource);
108+
String stringLiteralAstValue = compileTemplate(fieldResolverContext, resolverArgumentDefinition.getValue(), parentSource);
108109
if (StringUtils.equals(typename, Scalars.GraphQLString.getName()) ||
109110
StringUtils.equals(typename, Scalars.GraphQLID.getName())) {
110111
stringLiteralAstValue = String.format("\"%s\"", stringLiteralAstValue);
@@ -113,7 +114,7 @@ private Value<?> resolveArgumentValue(ResolverArgumentDefinition resolverArgumen
113114

114115
}
115116
} else {
116-
String stringLiteralAstValue = compileTemplate(resolverArgumentDefinition.getValue(), parentSource);
117+
String stringLiteralAstValue = compileTemplate(fieldResolverContext, resolverArgumentDefinition.getValue(), parentSource);
117118
return Parser.parseValue(stringLiteralAstValue);
118119
}
119120
}
@@ -152,8 +153,8 @@ private Field createSelectionSetFor(final DataFetchingEnvironment dataFetchingEn
152153
return currField;
153154
}
154155

155-
private String compileTemplate(String stringTemplate, Map<String, Object> dataSource) {
156-
ValueTemplate valueTemplate = new ValueTemplate(stringTemplate);
156+
private String compileTemplate(FieldResolverContext fieldResolverContext, String stringTemplate, Map<String, Object> dataSource) {
157+
ValueTemplate valueTemplate = new ValueTemplate(fieldResolverContext, stringTemplate);
157158
return valueTemplate.compile(dataSource);
158159
}
159160

src/main/java/com/intuit/graphql/orchestrator/fieldresolver/ValueTemplate.java

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,44 @@
11
package com.intuit.graphql.orchestrator.fieldresolver;
22

3-
import org.apache.velocity.Template;
4-
import org.apache.velocity.VelocityContext;
5-
import org.apache.velocity.app.Velocity;
6-
import org.apache.velocity.app.VelocityEngine;
7-
import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
8-
import org.apache.velocity.runtime.resource.util.StringResourceRepository;
9-
10-
import java.io.StringWriter;
3+
import com.intuit.graphql.orchestrator.schema.transform.FieldResolverContext;
4+
import org.apache.commons.collections4.CollectionUtils;
5+
import org.apache.commons.lang3.StringUtils;
6+
117
import java.util.Map;
128

13-
/**
14-
* This uses org.apache.velocity.app.VelocityEngine in a separate instance mode. Meaning this class should be instantiated
15-
* for every use.
16-
*/
179
public class ValueTemplate {
1810

19-
private final VelocityEngine engine = new VelocityEngine();
20-
21-
public ValueTemplate(String stringTemplate) {
22-
engine.setProperty(Velocity.RESOURCE_LOADERS, "string");
23-
engine.addProperty("resource.loader.string.class", StringResourceLoader.class.getName());
24-
engine.addProperty("resource.loader.string.repository.static", "false");
25-
engine.init();
11+
private FieldResolverContext fieldResolverContext;
12+
private String template;
13+
private boolean formatStringRef;
2614

27-
StringResourceRepository repo = (StringResourceRepository) engine.getApplicationAttribute(StringResourceLoader.REPOSITORY_NAME_DEFAULT);
28-
repo.putStringResource("1", stringTemplate);
15+
public ValueTemplate(FieldResolverContext fieldResolverContext, String template) {
16+
this.fieldResolverContext = fieldResolverContext;
17+
this.template = template;
18+
this.formatStringRef = StringUtils.containsAny(template, "[", "{");
2919
}
30-
3120
public String compile(Map<String, Object> dataSource) {
32-
VelocityContext context = new VelocityContext();
33-
34-
dataSource.keySet().stream()
35-
.forEach(key -> context.put(key, dataSource.get(key)));
36-
37-
// Get and merge the template with my parameters.
38-
Template template = engine.getTemplate("1");
39-
StringWriter writer = new StringWriter();
40-
template.merge(context, writer);
41-
42-
return writer.toString();
21+
String resolverReferenceTemplate1 = "\"$%s\"";
22+
String resolverReferenceTemplate2 = "$%s";
23+
24+
//Create as new String to not interfere with resolver reference
25+
return CollectionUtils.isNotEmpty(fieldResolverContext.getRequiredFields()) ? fieldResolverContext.getRequiredFields().stream().reduce(template, (formattedTemplate, resolverRef) -> {
26+
String stringValue;
27+
Object resolverValue = dataSource.get(resolverRef);
28+
if(resolverValue == null) {
29+
stringValue = "null";
30+
} else if(formatStringRef && resolverValue instanceof String) {
31+
stringValue = StringUtils.join("\"", resolverValue.toString(), "\"");
32+
} else {
33+
stringValue = resolverValue.toString();
34+
}
35+
36+
String resolverReference1 = String.format(resolverReferenceTemplate1, resolverRef);
37+
String resolverReference2 = String.format(resolverReferenceTemplate2, resolverRef);
38+
39+
String replaceQuotedRef = StringUtils.replace(formattedTemplate, resolverReference1, stringValue);
40+
return StringUtils.replace(replaceQuotedRef, resolverReference2, stringValue);
41+
}) : template;
4342
}
4443

4544
}

src/test/java/com/intuit/graphql/orchestrator/fieldresolver/FieldResolverBatchSelectionSetSupplierLiteralsTest.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
package com.intuit.graphql.orchestrator.fieldresolver;
22

3-
import static com.intuit.graphql.orchestrator.XtextObjectCreationUtil.buildFieldDefinition;
4-
import static com.intuit.graphql.orchestrator.XtextObjectCreationUtil.buildObjectTypeDefinition;
5-
import static java.util.Collections.singletonList;
6-
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
7-
import static org.mockito.Mockito.when;
8-
93
import com.intuit.graphql.graphQL.EnumTypeDefinition;
104
import com.intuit.graphql.graphQL.EnumValueDefinition;
115
import com.intuit.graphql.graphQL.FieldDefinition;
@@ -27,16 +21,23 @@
2721
import graphql.language.SelectionSet;
2822
import graphql.language.StringValue;
2923
import graphql.schema.DataFetchingEnvironment;
30-
import java.util.ArrayList;
31-
import java.util.HashMap;
32-
import java.util.List;
33-
import java.util.Map;
3424
import org.junit.Before;
3525
import org.junit.Test;
3626
import org.junit.runner.RunWith;
3727
import org.mockito.Mock;
3828
import org.mockito.junit.MockitoJUnitRunner;
3929

30+
import java.util.ArrayList;
31+
import java.util.HashMap;
32+
import java.util.List;
33+
import java.util.Map;
34+
35+
import static com.intuit.graphql.orchestrator.XtextObjectCreationUtil.buildFieldDefinition;
36+
import static com.intuit.graphql.orchestrator.XtextObjectCreationUtil.buildObjectTypeDefinition;
37+
import static java.util.Collections.singletonList;
38+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
39+
import static org.mockito.Mockito.when;
40+
4041
@RunWith(MockitoJUnitRunner.class)
4142
public class FieldResolverBatchSelectionSetSupplierLiteralsTest {
4243

@@ -94,6 +95,15 @@ public void testWithObjectLiteralsArgument() {
9495
new ResolverArgumentDefinition("petIdInputObject", "{ id : \"$petId\" }", objectType)
9596
));
9697

98+
testFieldResolverContext = FieldResolverContext.builder()
99+
.parentTypeDefinition(testFieldResolverContext.getParentTypeDefinition())
100+
.fieldDefinition(fieldDefinitionWithResolver)
101+
.requiresTypeNameInjection(true)
102+
.serviceNamespace("TESTSVC")
103+
.resolverDirectiveDefinition(resolverDirectiveDefinitionMock)
104+
.requiredFields(testDFEDataSource.keySet())
105+
.build();
106+
97107
String[] resolverSelectedFields = new String[] {"petById"};
98108

99109
// WHEN
Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,37 @@
11
package com.intuit.graphql.orchestrator.fieldresolver;
22

3-
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
3+
import com.google.common.collect.Sets;
4+
import com.intuit.graphql.orchestrator.schema.transform.FieldResolverContext;
5+
import org.junit.Before;
6+
import org.junit.Test;
7+
import org.mockito.Mockito;
48

59
import java.util.HashMap;
610
import java.util.Map;
7-
import org.junit.Test;
11+
import java.util.Set;
12+
13+
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
14+
import static org.mockito.Mockito.when;
815

916
public class ValueTemplateTest {
1017

11-
private Map<String, Object> testDataSource = new HashMap<>();
18+
private Map<String, Object> testDataSource;
19+
private FieldResolverContext fieldResolverContextMock;
20+
21+
@Before
22+
public void init() {
23+
fieldResolverContextMock = Mockito.mock(FieldResolverContext.class);
24+
testDataSource = new HashMap<>();
25+
}
1226

1327
@Test
1428
public void compile_SimpleVariable_success() {
1529
String valueTemplateStr = "$someVar";
16-
ValueTemplate subjectUnderTest = new ValueTemplate(valueTemplateStr);
30+
ValueTemplate subjectUnderTest = new ValueTemplate(fieldResolverContextMock, valueTemplateStr);
31+
32+
Set<String> requiredFields = Sets.newHashSet("someVar");
33+
when(fieldResolverContextMock.getRequiredFields()).thenReturn(requiredFields);
34+
1735
testDataSource.put("someVar", "TEST_VALUE");
1836

1937
String actual = subjectUnderTest.compile(testDataSource);
@@ -24,12 +42,55 @@ public void compile_SimpleVariable_success() {
2442
@Test
2543
public void compile_jsonStringWithVariable_success() {
2644
String valueTemplateStr = "{ id : \"$someVar\" }";
27-
ValueTemplate subjectUnderTest = new ValueTemplate(valueTemplateStr);
45+
ValueTemplate subjectUnderTest = new ValueTemplate(fieldResolverContextMock, valueTemplateStr);
2846
testDataSource.put("someVar", "TEST_VALUE");
2947

48+
Set<String> requiredFields = Sets.newHashSet("someVar");
49+
when(fieldResolverContextMock.getRequiredFields()).thenReturn(requiredFields);
50+
3051
String actual = subjectUnderTest.compile(testDataSource);
3152

3253
assertThat(actual).isEqualTo("{ id : \"TEST_VALUE\" }");
3354
}
3455

56+
@Test
57+
public void compile_jsonStringWithNullVariable_success() {
58+
String valueTemplateStr = "{ id : \"$someVar\" }";
59+
ValueTemplate subjectUnderTest = new ValueTemplate(fieldResolverContextMock, valueTemplateStr);
60+
testDataSource.put("someVar", null);
61+
62+
Set<String> requiredFields = Sets.newHashSet("someVar");
63+
when(fieldResolverContextMock.getRequiredFields()).thenReturn(requiredFields);
64+
65+
String actual = subjectUnderTest.compile(testDataSource);
66+
assertThat(actual).isEqualTo("{ id : null }");
67+
}
68+
@Test
69+
public void compile_jsonStringWithMultipleVariables_success() {
70+
String valueTemplateStr = "{ id : \"$petId\" name : \"$petName\" }";
71+
ValueTemplate subjectUnderTest = new ValueTemplate(fieldResolverContextMock, valueTemplateStr);
72+
testDataSource.put("petId", "pet-901");
73+
testDataSource.put("petName", null);
74+
75+
Set<String> requiredFields = Sets.newHashSet("petId", "petName");
76+
when(fieldResolverContextMock.getRequiredFields()).thenReturn(requiredFields);
77+
78+
String actual = subjectUnderTest.compile(testDataSource);
79+
80+
assertThat(actual).isEqualTo("{ id : \"pet-901\" name : null }");
81+
}
82+
83+
@Test
84+
public void compile_jsonStringWithMultipleVariablesNotString_success() {
85+
String valueTemplateStr = "{ includeName : \"$includeName\" name : \"$childCount\" }";
86+
ValueTemplate subjectUnderTest = new ValueTemplate(fieldResolverContextMock, valueTemplateStr);
87+
testDataSource.put("includeName", true);
88+
testDataSource.put("childCount", 5);
89+
90+
Set<String> requiredFields = Sets.newHashSet("includeName", "childCount");
91+
when(fieldResolverContextMock.getRequiredFields()).thenReturn(requiredFields);
92+
93+
String actual = subjectUnderTest.compile(testDataSource);
94+
assertThat(actual).isEqualTo("{ includeName : true name : 5 }");
95+
}
3596
}

0 commit comments

Comments
 (0)