Skip to content

Commit 663f8b5

Browse files
authored
Refine FPD (#3653)
1 parent 6b7fecb commit 663f8b5

8 files changed

Lines changed: 388 additions & 624 deletions

File tree

Lines changed: 22 additions & 207 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,20 @@
11
package org.prebid.server.auction;
22

3-
import com.fasterxml.jackson.core.type.TypeReference;
3+
import com.fasterxml.jackson.core.JsonProcessingException;
44
import com.fasterxml.jackson.databind.JsonNode;
5-
import com.fasterxml.jackson.databind.node.ArrayNode;
5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import com.fasterxml.jackson.databind.node.NullNode;
67
import com.fasterxml.jackson.databind.node.ObjectNode;
78
import com.iab.openrtb.request.App;
8-
import com.iab.openrtb.request.Data;
99
import com.iab.openrtb.request.Dooh;
1010
import com.iab.openrtb.request.Site;
1111
import com.iab.openrtb.request.User;
12-
import org.apache.commons.lang3.ObjectUtils;
12+
import org.prebid.server.exception.InvalidRequestException;
1313
import org.prebid.server.json.JacksonMapper;
1414
import org.prebid.server.json.JsonMerger;
15-
import org.prebid.server.proto.openrtb.ext.request.ExtApp;
16-
import org.prebid.server.proto.openrtb.ext.request.ExtDooh;
17-
import org.prebid.server.proto.openrtb.ext.request.ExtSite;
18-
import org.prebid.server.proto.openrtb.ext.request.ExtUser;
1915

20-
import java.util.Collections;
21-
import java.util.List;
2216
import java.util.Objects;
2317
import java.util.Set;
24-
import java.util.function.Function;
25-
import java.util.function.Predicate;
26-
import java.util.stream.StreamSupport;
2718

2819
public class FpdResolver {
2920

@@ -33,17 +24,8 @@ public class FpdResolver {
3324
private static final String APP = "app";
3425
private static final String DOOH = "dooh";
3526
private static final Set<String> KNOWN_FPD_ATTRIBUTES = Set.of(USER, SITE, APP, DOOH, BIDDERS);
36-
private static final String EXT = "ext";
3727
private static final String CONTEXT = "context";
3828
private static final String DATA = "data";
39-
private static final Set<String> USER_DATA_ATTR = Collections.singleton("geo");
40-
private static final Set<String> APP_DATA_ATTR = Set.of("id", "content", "publisher", "privacypolicy");
41-
private static final Set<String> SITE_DATA_ATTR = Set.of("id", "content", "publisher", "privacypolicy", "mobile");
42-
private static final Set<String> DOOH_DATA_ATTR = Set.of("id", "content", "publisher", "privacypolicy");
43-
44-
private static final TypeReference<List<Data>> USER_DATA_TYPE_REFERENCE =
45-
new TypeReference<>() {
46-
};
4729

4830
private final JacksonMapper jacksonMapper;
4931
private final JsonMerger jsonMerger;
@@ -54,146 +36,37 @@ public FpdResolver(JacksonMapper jacksonMapper, JsonMerger jsonMerger) {
5436
}
5537

5638
public User resolveUser(User originUser, ObjectNode fpdUser) {
57-
if (fpdUser == null) {
58-
return originUser;
59-
}
60-
final User resultUser = originUser == null ? User.builder().build() : originUser;
61-
final ExtUser resolvedExtUser = resolveUserExt(fpdUser, resultUser);
62-
return resultUser.toBuilder()
63-
.keywords(ObjectUtils.defaultIfNull(getString(fpdUser, "keywords"), resultUser.getKeywords()))
64-
.gender(ObjectUtils.defaultIfNull(getString(fpdUser, "gender"), resultUser.getGender()))
65-
.yob(ObjectUtils.defaultIfNull(getInteger(fpdUser, "yob"), resultUser.getYob()))
66-
.data(ObjectUtils.defaultIfNull(getFpdUserData(fpdUser), resultUser.getData()))
67-
.ext(resolvedExtUser)
68-
.build();
69-
}
70-
71-
private ExtUser resolveUserExt(ObjectNode fpdUser, User originUser) {
72-
final ExtUser originExtUser = originUser.getExt();
73-
final ObjectNode resolvedData =
74-
mergeExtData(fpdUser.path(EXT).path(DATA), originExtUser != null ? originExtUser.getData() : null);
75-
76-
return updateUserExtDataWithFpdAttr(fpdUser, originExtUser, resolvedData);
77-
}
78-
79-
private ExtUser updateUserExtDataWithFpdAttr(ObjectNode fpdUser, ExtUser originExtUser, ObjectNode extData) {
80-
final ObjectNode resultData = extData != null ? extData : jacksonMapper.mapper().createObjectNode();
81-
USER_DATA_ATTR.forEach(attribute -> setAttr(fpdUser, resultData, attribute));
82-
return originExtUser != null
83-
? originExtUser.toBuilder().data(resultData.isEmpty() ? null : resultData).build()
84-
: resultData.isEmpty() ? null : ExtUser.builder().data(resultData).build();
85-
}
86-
87-
private List<Data> getFpdUserData(ObjectNode fpdUser) {
88-
final ArrayNode fpdUserDataNode = getValueFromJsonNode(
89-
fpdUser, DATA, node -> (ArrayNode) node, JsonNode::isArray);
90-
91-
return toList(fpdUserDataNode, USER_DATA_TYPE_REFERENCE);
39+
return mergeFpd(originUser, fpdUser, User.class);
9240
}
9341

9442
public App resolveApp(App originApp, ObjectNode fpdApp) {
95-
if (fpdApp == null) {
96-
return originApp;
97-
}
98-
final App resultApp = originApp == null ? App.builder().build() : originApp;
99-
final ExtApp resolvedExtApp = resolveAppExt(fpdApp, resultApp);
100-
return resultApp.toBuilder()
101-
.name(ObjectUtils.defaultIfNull(getString(fpdApp, "name"), resultApp.getName()))
102-
.bundle(ObjectUtils.defaultIfNull(getString(fpdApp, "bundle"), resultApp.getBundle()))
103-
.storeurl(ObjectUtils.defaultIfNull(getString(fpdApp, "storeurl"), resultApp.getStoreurl()))
104-
.domain(ObjectUtils.defaultIfNull(getString(fpdApp, "domain"), resultApp.getDomain()))
105-
.cat(ObjectUtils.defaultIfNull(getStrings(fpdApp, "cat"), resultApp.getCat()))
106-
.sectioncat(ObjectUtils.defaultIfNull(getStrings(fpdApp, "sectioncat"), resultApp.getSectioncat()))
107-
.pagecat(ObjectUtils.defaultIfNull(getStrings(fpdApp, "pagecat"), resultApp.getPagecat()))
108-
.keywords(ObjectUtils.defaultIfNull(getString(fpdApp, "keywords"), resultApp.getKeywords()))
109-
.ext(resolvedExtApp)
110-
.build();
111-
}
112-
113-
private ExtApp resolveAppExt(ObjectNode fpdApp, App originApp) {
114-
final ExtApp originExtApp = originApp.getExt();
115-
final ObjectNode resolvedData =
116-
mergeExtData(fpdApp.path(EXT).path(DATA), originExtApp != null ? originExtApp.getData() : null);
117-
118-
return updateAppExtDataWithFpdAttr(fpdApp, originExtApp, resolvedData);
119-
}
120-
121-
private ExtApp updateAppExtDataWithFpdAttr(ObjectNode fpdApp, ExtApp originExtApp, ObjectNode extData) {
122-
final ObjectNode resultData = extData != null ? extData : jacksonMapper.mapper().createObjectNode();
123-
APP_DATA_ATTR.forEach(attribute -> setAttr(fpdApp, resultData, attribute));
124-
return originExtApp != null
125-
? ExtApp.of(originExtApp.getPrebid(), resultData.isEmpty() ? null : resultData)
126-
: resultData.isEmpty() ? null : ExtApp.of(null, resultData);
43+
return mergeFpd(originApp, fpdApp, App.class);
12744
}
12845

12946
public Site resolveSite(Site originSite, ObjectNode fpdSite) {
130-
if (fpdSite == null) {
131-
return originSite;
132-
}
133-
final Site resultSite = originSite == null ? Site.builder().build() : originSite;
134-
final ExtSite resolvedExtSite = resolveSiteExt(fpdSite, resultSite);
135-
return resultSite.toBuilder()
136-
.name(ObjectUtils.defaultIfNull(getString(fpdSite, "name"), resultSite.getName()))
137-
.domain(ObjectUtils.defaultIfNull(getString(fpdSite, "domain"), resultSite.getDomain()))
138-
.cat(ObjectUtils.defaultIfNull(getStrings(fpdSite, "cat"), resultSite.getCat()))
139-
.sectioncat(ObjectUtils.defaultIfNull(getStrings(fpdSite, "sectioncat"), resultSite.getSectioncat()))
140-
.pagecat(ObjectUtils.defaultIfNull(getStrings(fpdSite, "pagecat"), resultSite.getPagecat()))
141-
.page(ObjectUtils.defaultIfNull(getString(fpdSite, "page"), resultSite.getPage()))
142-
.keywords(ObjectUtils.defaultIfNull(getString(fpdSite, "keywords"), resultSite.getKeywords()))
143-
.ref(ObjectUtils.defaultIfNull(getString(fpdSite, "ref"), resultSite.getRef()))
144-
.search(ObjectUtils.defaultIfNull(getString(fpdSite, "search"), resultSite.getSearch()))
145-
.ext(resolvedExtSite)
146-
.build();
147-
}
148-
149-
private ExtSite resolveSiteExt(ObjectNode fpdSite, Site originSite) {
150-
final ExtSite originExtSite = originSite.getExt();
151-
final ObjectNode resolvedData =
152-
mergeExtData(fpdSite.path(EXT).path(DATA), originExtSite != null ? originExtSite.getData() : null);
153-
154-
return updateSiteExtDataWithFpdAttr(fpdSite, originExtSite, resolvedData);
155-
}
156-
157-
private ExtSite updateSiteExtDataWithFpdAttr(ObjectNode fpdSite, ExtSite originExtSite, ObjectNode extData) {
158-
final ObjectNode resultData = extData != null ? extData : jacksonMapper.mapper().createObjectNode();
159-
SITE_DATA_ATTR.forEach(attribute -> setAttr(fpdSite, resultData, attribute));
160-
return originExtSite != null
161-
? ExtSite.of(originExtSite.getAmp(), resultData.isEmpty() ? null : resultData)
162-
: resultData.isEmpty() ? null : ExtSite.of(null, resultData);
47+
return mergeFpd(originSite, fpdSite, Site.class);
16348
}
16449

16550
public Dooh resolveDooh(Dooh originDooh, ObjectNode fpdDooh) {
166-
if (fpdDooh == null) {
167-
return originDooh;
168-
}
169-
final Dooh resultDooh = originDooh == null ? Dooh.builder().build() : originDooh;
170-
final ExtDooh resolvedExtDooh = resolveDoohExt(fpdDooh, resultDooh);
171-
return resultDooh.toBuilder()
172-
.name(ObjectUtils.defaultIfNull(getString(fpdDooh, "name"), resultDooh.getName()))
173-
.venuetype(ObjectUtils.defaultIfNull(getStrings(fpdDooh, "venuetype"), resultDooh.getVenuetype()))
174-
.venuetypetax(ObjectUtils.defaultIfNull(
175-
getInteger(fpdDooh, "venuetypetax"),
176-
resultDooh.getVenuetypetax()))
177-
.domain(ObjectUtils.defaultIfNull(getString(fpdDooh, "domain"), resultDooh.getDomain()))
178-
.keywords(ObjectUtils.defaultIfNull(getString(fpdDooh, "keywords"), resultDooh.getKeywords()))
179-
.ext(resolvedExtDooh)
180-
.build();
51+
return mergeFpd(originDooh, fpdDooh, Dooh.class);
18152
}
18253

183-
private ExtDooh resolveDoohExt(ObjectNode fpdDooh, Dooh originDooh) {
184-
final ExtDooh originExtDooh = originDooh.getExt();
185-
final ObjectNode resolvedData =
186-
mergeExtData(fpdDooh.path(EXT).path(DATA), originExtDooh != null ? originExtDooh.getData() : null);
54+
private <T> T mergeFpd(T original, ObjectNode fpd, Class<T> tClass) {
55+
if (fpd == null || fpd.isNull() || fpd.isMissingNode()) {
56+
return original;
57+
}
18758

188-
return updateDoohExtDataWithFpdAttr(fpdDooh, originExtDooh, resolvedData);
189-
}
59+
final ObjectMapper mapper = jacksonMapper.mapper();
19060

191-
private ExtDooh updateDoohExtDataWithFpdAttr(ObjectNode fpdDooh, ExtDooh originExtDooh, ObjectNode extData) {
192-
final ObjectNode resultData = extData != null ? extData : jacksonMapper.mapper().createObjectNode();
193-
DOOH_DATA_ATTR.forEach(attribute -> setAttr(fpdDooh, resultData, attribute));
194-
return originExtDooh != null
195-
? ExtDooh.of(resultData.isEmpty() ? null : resultData)
196-
: resultData.isEmpty() ? null : ExtDooh.of(resultData);
61+
final JsonNode originalAsJsonNode = original != null
62+
? mapper.valueToTree(original)
63+
: NullNode.getInstance();
64+
final JsonNode merged = jsonMerger.merge(fpd, originalAsJsonNode);
65+
try {
66+
return mapper.treeToValue(merged, tClass);
67+
} catch (JsonProcessingException e) {
68+
throw new InvalidRequestException("Can't convert merging result class " + tClass.getName());
69+
}
19770
}
19871

19972
public ObjectNode resolveImpExt(ObjectNode impExt, ObjectNode targeting) {
@@ -277,62 +150,4 @@ private void removeOrReplace(ObjectNode impExt, String field, JsonNode jsonNode)
277150
impExt.set(field, jsonNode);
278151
}
279152
}
280-
281-
private ObjectNode mergeExtData(JsonNode fpdData, JsonNode originData) {
282-
if (fpdData.isMissingNode() || !fpdData.isObject()) {
283-
return originData != null && originData.isObject() ? ((ObjectNode) originData).deepCopy() : null;
284-
}
285-
286-
if (originData != null && originData.isObject()) {
287-
return (ObjectNode) jsonMerger.merge(fpdData, originData);
288-
}
289-
return fpdData.isObject() ? (ObjectNode) fpdData : null;
290-
}
291-
292-
private static void setAttr(ObjectNode source, ObjectNode dest, String fieldName) {
293-
final JsonNode field = source.get(fieldName);
294-
if (field != null) {
295-
dest.set(fieldName, field);
296-
}
297-
}
298-
299-
private static List<String> getStrings(JsonNode firstItem, String fieldName) {
300-
final JsonNode valueNode = firstItem.get(fieldName);
301-
final ArrayNode arrayNode = valueNode != null && valueNode.isArray() ? (ArrayNode) valueNode : null;
302-
return arrayNode != null && isTextualArray(arrayNode)
303-
? StreamSupport.stream(arrayNode.spliterator(), false)
304-
.map(JsonNode::asText)
305-
.toList()
306-
: null;
307-
}
308-
309-
private static boolean isTextualArray(ArrayNode arrayNode) {
310-
return StreamSupport.stream(arrayNode.spliterator(), false).allMatch(JsonNode::isTextual);
311-
}
312-
313-
private static String getString(ObjectNode firstItem, String fieldName) {
314-
return getValueFromJsonNode(firstItem, fieldName, JsonNode::asText, JsonNode::isTextual);
315-
}
316-
317-
private static Integer getInteger(ObjectNode firstItem, String fieldName) {
318-
return getValueFromJsonNode(firstItem, fieldName, JsonNode::asInt, JsonNode::isInt);
319-
}
320-
321-
private <T> List<T> toList(JsonNode node, TypeReference<List<T>> listTypeReference) {
322-
try {
323-
return jacksonMapper.mapper().convertValue(node, listTypeReference);
324-
} catch (IllegalArgumentException e) {
325-
return null;
326-
}
327-
}
328-
329-
private static <T> T getValueFromJsonNode(ObjectNode firstItem, String fieldName,
330-
Function<JsonNode, T> nodeConverter,
331-
Predicate<JsonNode> isCorrectType) {
332-
final JsonNode valueNode = firstItem.get(fieldName);
333-
return valueNode != null && isCorrectType.test(valueNode)
334-
? nodeConverter.apply(valueNode)
335-
: null;
336-
}
337-
338153
}

0 commit comments

Comments
 (0)