Skip to content

Commit 40c384d

Browse files
authored
add getFieldValue method to FieldMasks (#387)
1 parent 63bf76a commit 40c384d

2 files changed

Lines changed: 167 additions & 2 deletions

File tree

google-ads/src/main/java/com/google/ads/googleads/lib/utils/FieldMasks.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,17 @@
1414

1515
package com.google.ads.googleads.lib.utils;
1616

17+
import static com.google.protobuf.Descriptors.FieldDescriptor.JavaType.MESSAGE;
18+
1719
import com.google.common.base.Preconditions;
1820
import com.google.protobuf.Descriptors.Descriptor;
1921
import com.google.protobuf.Descriptors.FieldDescriptor;
2022
import com.google.protobuf.FieldMask;
2123
import com.google.protobuf.GeneratedMessageV3;
2224
import com.google.protobuf.Message;
25+
import java.util.Arrays;
26+
import java.util.Deque;
27+
import java.util.LinkedList;
2328
import java.util.Objects;
2429

2530
/** Utility methods for working with field masks. */
@@ -119,6 +124,56 @@ private static void compare(
119124
}
120125
}
121126

127+
/**
128+
* Gets the field value of a message from a field mask path.
129+
*
130+
* @param fieldMaskPath The field mask path.
131+
* @param entity The entity to retrieve values from.
132+
* @return the field Object.
133+
*/
134+
public static <V> V getFieldValue(String fieldMaskPath, Message entity) {
135+
Deque<String> fieldMaskParts =
136+
new LinkedList<>(Arrays.asList(fieldMaskPath.trim().split("\\.")));
137+
Message currentEntity = entity;
138+
while (!fieldMaskParts.isEmpty()) {
139+
String fieldName = fieldMaskParts.remove();
140+
Preconditions.checkNotNull(
141+
currentEntity, String.format("Cannot get field value. %s is null", fieldName));
142+
Descriptor descriptor = currentEntity.getDescriptorForType();
143+
FieldDescriptor childField = descriptor.findFieldByName(fieldName);
144+
145+
if (childField == null) {
146+
throw new IllegalArgumentException(
147+
String.format(
148+
"Cannot retrieve field value. A matching field was not found after"
149+
+ " navigating %s up to %s",
150+
fieldMaskPath, fieldName));
151+
}
152+
Object childValue = currentEntity.getField(childField);
153+
if (fieldMaskParts.isEmpty()) {
154+
return (V) childValue;
155+
} else if (childValue == null) {
156+
throw new IllegalArgumentException(
157+
String.format(
158+
"Attempt to access a sub-field of %s in path %s which is null",
159+
fieldName, fieldMaskPath));
160+
} else if (childField.isRepeated()) {
161+
throw new IllegalArgumentException(
162+
String.format(
163+
"Cannot access repeated sub-field %s in path %s of %s",
164+
fieldName, fieldMaskPath, currentEntity.getClass()));
165+
} else if (childField.getJavaType() == MESSAGE) {
166+
currentEntity = (Message) childValue;
167+
} else {
168+
throw new IllegalArgumentException(
169+
String.format(
170+
"Cannot access a field nested inside a primitive or enum %s in path %s in %s",
171+
fieldName, fieldMaskPath, currentEntity.getClass()));
172+
}
173+
}
174+
return (V) currentEntity;
175+
}
176+
122177
private static String getFieldName(String currentField, FieldDescriptor field) {
123178
if (currentField.isEmpty()) {
124179
return field.getName();
Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,134 @@
11
package com.google.ads.googleads.lib.utils;
22

33
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertThrows;
45

5-
import com.google.ads.googleads.v5.resources.Campaign;
6+
import com.google.ads.googleads.v6.common.ManualCpc;
7+
import com.google.ads.googleads.v6.enums.CampaignStatusEnum.CampaignStatus;
8+
import com.google.ads.googleads.v6.resources.Campaign;
69
import com.google.common.collect.ImmutableList;
10+
import com.google.protobuf.Descriptors.EnumValueDescriptor;
11+
import org.junit.Before;
712
import org.junit.Test;
813
import org.junit.runner.RunWith;
914
import org.junit.runners.JUnit4;
1015

1116
@RunWith(JUnit4.class)
1217
public class FieldMasksTest {
18+
private Campaign campaign;
19+
20+
@Before
21+
public void setUp() {
22+
Campaign.Builder builder =
23+
Campaign.newBuilder().setName("New campaign").setStatus(CampaignStatus.PAUSED);
24+
builder.getManualCpcBuilder().setEnhancedCpcEnabled(false);
25+
campaign = builder.build();
26+
}
1327

1428
// Regression test for https://github.com/googleads/google-ads-java/issues/344.
1529
@Test
1630
public void optionalField_withDefaultValue_detectsChange() {
1731
Campaign.Builder builder = Campaign.newBuilder();
1832
builder.getManualCpcBuilder().setEnhancedCpcEnabled(false);
19-
Campaign campaign = builder.build();
33+
campaign = builder.build();
2034
assertEquals(
2135
ImmutableList.of("manual_cpc.enhanced_cpc_enabled"),
2236
FieldMasks.allSetFieldsOf(campaign).getPathsList());
2337
}
38+
39+
@Test
40+
public void getFieldValue_gets_basic_field() {
41+
assertEquals(FieldMasks.getFieldValue("name", campaign), campaign.getName());
42+
}
43+
44+
@Test
45+
public void getFieldValue_assigns_basic_field() {
46+
String name = FieldMasks.getFieldValue("name", campaign);
47+
Campaign campaignToCompare = campaign.toBuilder().setName(name).build();
48+
assertEquals(campaign, campaignToCompare);
49+
}
50+
51+
@Test
52+
public void getFieldValue_gets_nested_field() {
53+
assertEquals(
54+
FieldMasks.getFieldValue("manual_cpc.enhanced_cpc_enabled", campaign),
55+
campaign.getManualCpc().getEnhancedCpcEnabled());
56+
}
57+
58+
@Test
59+
public void getFieldValue_assigns_nested_field() {
60+
boolean enhancedCpcEnabled =
61+
FieldMasks.getFieldValue("manual_cpc.enhanced_cpc_enabled", campaign);
62+
Campaign.Builder campaignToCompare = campaign.toBuilder();
63+
campaignToCompare.getManualCpcBuilder().setEnhancedCpcEnabled(enhancedCpcEnabled);
64+
assertEquals(campaign, campaignToCompare.build());
65+
}
66+
67+
@Test
68+
public void getFieldValue_gets_top_nested_field() {
69+
assertEquals(FieldMasks.getFieldValue("manual_cpc", campaign), campaign.getManualCpc());
70+
}
71+
72+
@Test
73+
public void getFieldValue_assigns_top_nested_field() {
74+
ManualCpc manualCpc = FieldMasks.getFieldValue("manual_cpc", campaign);
75+
Campaign campaignToCompare = campaign.toBuilder().setManualCpc(manualCpc).build();
76+
assertEquals(campaign, campaignToCompare);
77+
}
78+
79+
@Test
80+
public void getFieldValue_gets_enum_field() {
81+
assertEquals(
82+
FieldMasks.getFieldValue("status", campaign), campaign.getStatus().getValueDescriptor());
83+
}
84+
85+
@Test
86+
public void getFieldValue_assigns_enum_value() {
87+
EnumValueDescriptor status = FieldMasks.getFieldValue("status", campaign);
88+
Campaign.Builder campaignToCompare = campaign.toBuilder();
89+
campaignToCompare.setStatus(CampaignStatus.forNumber(status.getNumber()));
90+
assertEquals(campaign, campaignToCompare.build());
91+
}
92+
93+
@Test
94+
public void getFieldValue_no_match_throws_exception() {
95+
IllegalArgumentException exception =
96+
assertThrows(
97+
IllegalArgumentException.class,
98+
() -> {
99+
FieldMasks.getFieldValue("foo.bar", campaign);
100+
});
101+
assertEquals(
102+
exception.getMessage(),
103+
"Cannot retrieve field value. A matching field was not found after navigating foo.bar up to"
104+
+ " foo");
105+
}
106+
107+
@Test
108+
public void getFieldValue_repeated_throws_exception() {
109+
IllegalArgumentException exception =
110+
assertThrows(
111+
IllegalArgumentException.class,
112+
() -> {
113+
FieldMasks.getFieldValue("url_custom_parameters.key", campaign);
114+
});
115+
assertEquals(
116+
exception.getMessage(),
117+
"Cannot access repeated sub-field url_custom_parameters in path url_custom_parameters.key"
118+
+ " of class com.google.ads.googleads.v6.resources.Campaign");
119+
}
120+
121+
@Test
122+
public void getFieldValue_non_message_throws_exception() {
123+
IllegalArgumentException exception =
124+
assertThrows(
125+
IllegalArgumentException.class,
126+
() -> {
127+
FieldMasks.getFieldValue("status.paused", campaign);
128+
});
129+
assertEquals(
130+
exception.getMessage(),
131+
"Cannot access a field nested inside a primitive or enum status in path status.paused in"
132+
+ " class com.google.ads.googleads.v6.resources.Campaign");
133+
}
24134
}

0 commit comments

Comments
 (0)