Skip to content

Commit 844a3cb

Browse files
committed
Added validation of live vendor list and removed exception on empty deleted vendors
1 parent 8aa7fdc commit 844a3cb

2 files changed

Lines changed: 51 additions & 39 deletions

File tree

src/main/java/org/prebid/server/privacy/gdpr/vendorlist/LiveVendorListService.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import io.vertx.core.Promise;
44
import io.vertx.core.Vertx;
5-
import org.apache.commons.collections4.CollectionUtils;
65
import org.prebid.server.exception.PreBidException;
76
import org.prebid.server.json.JacksonMapper;
87
import org.prebid.server.log.Logger;
@@ -15,7 +14,6 @@
1514
import org.prebid.server.vertx.httpclient.HttpClient;
1615
import org.prebid.server.vertx.httpclient.model.HttpClientResponse;
1716

18-
import java.io.IOException;
1917
import java.time.Clock;
2018
import java.time.Instant;
2119
import java.util.Objects;
@@ -83,11 +81,13 @@ private VendorList processResponse(HttpClientResponse response) {
8381
}
8482

8583
final String body = response.getBody();
86-
try {
87-
return mapper.mapper().readValue(body, VendorList.class);
88-
} catch (IOException e) {
89-
throw new PreBidException("Cannot parse live vendor list: " + body, e);
84+
final VendorList vendorList = VendorListUtil.parseVendorList(body, mapper);
85+
86+
if (!VendorListUtil.vendorListIsValid(vendorList)) {
87+
throw new PreBidException("Fetched vendor list parsed but has invalid data: " + body);
9088
}
89+
90+
return vendorList;
9191
}
9292

9393
Set<Integer> extractDeletedVendorIds(VendorList vendorList) {
@@ -105,10 +105,6 @@ private static boolean isDeletedAt(Vendor vendor, Instant now) {
105105
}
106106

107107
private Void updateDeletedVendorIds(Set<Integer> ids) {
108-
if (CollectionUtils.isEmpty(ids)) {
109-
throw new PreBidException("Live GVL response has no deleted vendors");
110-
}
111-
112108
deletedVendorIds = ids;
113109
metrics.updatePrivacyTcfVendorListLatestOkMetric();
114110
return null;

src/test/java/org/prebid/server/privacy/gdpr/vendorlist/LiveVendorListServiceTest.java

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
import org.mockito.junit.jupiter.MockitoExtension;
1111
import org.prebid.server.VertxTest;
1212
import org.prebid.server.metric.Metrics;
13+
import org.prebid.server.privacy.gdpr.vendorlist.proto.Feature;
14+
import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode;
15+
import org.prebid.server.privacy.gdpr.vendorlist.proto.SpecialFeature;
16+
import org.prebid.server.privacy.gdpr.vendorlist.proto.SpecialPurpose;
1317
import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor;
1418
import org.prebid.server.privacy.gdpr.vendorlist.proto.VendorList;
1519
import org.prebid.server.vertx.httpclient.HttpClient;
@@ -18,17 +22,20 @@
1822
import java.time.Clock;
1923
import java.time.Instant;
2024
import java.time.ZoneOffset;
25+
import java.util.Arrays;
2126
import java.util.Date;
22-
import java.util.HashMap;
23-
import java.util.Map;
27+
import java.util.EnumSet;
28+
import java.util.function.Function;
29+
import java.util.stream.Collectors;
2430

25-
import static java.util.Collections.emptyMap;
2631
import static org.assertj.core.api.Assertions.assertThat;
2732
import static org.mockito.ArgumentMatchers.anyLong;
2833
import static org.mockito.ArgumentMatchers.anyString;
2934
import static org.mockito.BDDMockito.given;
3035
import static org.mockito.Mockito.never;
3136
import static org.mockito.Mockito.verify;
37+
import static org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode.ONE;
38+
import static org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode.TWO;
3239

3340
@ExtendWith(MockitoExtension.class)
3441
public class LiveVendorListServiceTest extends VertxTest {
@@ -68,7 +75,8 @@ public void isDeletedShouldReturnFalseWhenFetchNeverSucceeded() {
6875
@Test
6976
public void isDeletedShouldReturnTrueWhenVendorIsDeletedInLiveVendorList() throws JsonProcessingException {
7077
// given
71-
final String responseBody = givenLiveGvlJson(Map.of(42, "2024-01-01T00:00:00Z"));
78+
final Vendor vendor = givenVendor(42, "2024-01-01T00:00:00Z");
79+
final String responseBody = mapper.writeValueAsString(givenVendorList(vendor));
7280
given(httpClient.get(anyString(), anyLong()))
7381
.willReturn(Future.succeededFuture(HttpClientResponse.of(200, null, responseBody)));
7482

@@ -83,11 +91,11 @@ public void isDeletedShouldReturnTrueWhenVendorIsDeletedInLiveVendorList() throw
8391
@Test
8492
public void extractDeletedVendorIdsShouldReturnOnlyVendorsWithPastDeletedDate() {
8593
// given
86-
final VendorList vendorList = givenVendorList(Map.of(
87-
1, givenVendor(1, "2024-01-01T00:00:00Z"),
88-
2, givenVendor(2, null),
89-
3, givenVendor(3, "2025-01-01T00:00:00Z"),
90-
4, givenVendor(4, "2024-06-01T12:00:00Z")));
94+
final VendorList vendorList = givenVendorList(
95+
givenVendor(1, "2024-01-01T00:00:00Z"),
96+
givenVendor(2, null),
97+
givenVendor(3, "2025-01-01T00:00:00Z"),
98+
givenVendor(4, "2024-06-01T12:00:00Z"));
9199

92100
// when
93101
final var deletedIds = target.extractDeletedVendorIds(vendorList);
@@ -99,7 +107,9 @@ public void extractDeletedVendorIdsShouldReturnOnlyVendorsWithPastDeletedDate()
99107
@Test
100108
public void refreshShouldUpdateDeletedVendorIdsAndIncrementOkMetric() throws JsonProcessingException {
101109
// given
102-
givenHttpClientReturnsResponse(200, givenLiveGvlJson(Map.of(1, "2024-01-01T00:00:00Z")));
110+
final Vendor vendor = givenVendor(1, "2024-01-01T00:00:00Z");
111+
final String responseBody = mapper.writeValueAsString(givenVendorList(vendor));
112+
givenHttpClientReturnsResponse(200, responseBody);
103113

104114
// when
105115
target.refresh();
@@ -113,12 +123,14 @@ public void refreshShouldUpdateDeletedVendorIdsAndIncrementOkMetric() throws Jso
113123
@Test
114124
public void refreshShouldReplaceDeletedVendorIdsOnSubsequentSuccessfulFetch() throws JsonProcessingException {
115125
// given
126+
final Vendor vendor = givenVendor(1, "2024-01-01T00:00:00Z");
127+
final String responseBody = mapper.writeValueAsString(givenVendorList(vendor));
128+
final Vendor vendor2 = givenVendor(2, "2024-02-01T00:00:00Z");
129+
final String responseBody2 = mapper.writeValueAsString(givenVendorList(vendor2));
116130
given(httpClient.get(anyString(), anyLong()))
117131
.willReturn(
118-
Future.succeededFuture(HttpClientResponse.of(
119-
200, null, givenLiveGvlJson(Map.of(1, "2024-01-01T00:00:00Z")))),
120-
Future.succeededFuture(HttpClientResponse.of(
121-
200, null, givenLiveGvlJson(Map.of(2, "2024-02-01T00:00:00Z")))));
132+
Future.succeededFuture(HttpClientResponse.of(200, null, responseBody)),
133+
Future.succeededFuture(HttpClientResponse.of(200, null, responseBody2)));
122134

123135
// when
124136
target.refresh();
@@ -171,26 +183,29 @@ public void refreshShouldIncrementErrorMetricOnInvalidJson() {
171183
}
172184

173185
@Test
174-
public void refreshShouldIncrementErrorMetricWhenNoDeletedVendorsInResponse() throws JsonProcessingException {
186+
public void refreshShouldIncrementErrorMetricOnInvalidVendorList() throws JsonProcessingException {
175187
// given
176-
givenHttpClientReturnsResponse(200, givenLiveGvlJson(emptyMap()));
188+
final Vendor vendor = givenVendor(42, "2024-01-01T00:00:00Z")
189+
.toBuilder().features(null).build();
190+
final String responseBody = mapper.writeValueAsString(givenVendorList(vendor));
191+
givenHttpClientReturnsResponse(200, responseBody);
177192

178193
// when
179194
target.refresh();
180195

181196
// then
182197
assertThat(target.isDeleted(1)).isFalse();
183198
verify(metrics).updatePrivacyTcfVendorListLatestErrorMetric();
184-
verify(metrics, never()).updatePrivacyTcfVendorListLatestOkMetric();
185199
}
186200

187201
@Test
188202
public void refreshShouldKeepLastGoodSetOnFailureAfterSuccessfulFetch() throws JsonProcessingException {
189203
// given
204+
final Vendor vendor = givenVendor(1, "2024-01-01T00:00:00Z");
205+
final String responseBody = mapper.writeValueAsString(givenVendorList(vendor));
190206
given(httpClient.get(anyString(), anyLong()))
191207
.willReturn(
192-
Future.succeededFuture(HttpClientResponse.of(
193-
200, null, givenLiveGvlJson(Map.of(1, "2024-01-01T00:00:00Z")))),
208+
Future.succeededFuture(HttpClientResponse.of(200, null, responseBody)),
194209
Future.failedFuture(new RuntimeException("connection failed")));
195210

196211
// when
@@ -208,22 +223,23 @@ private void givenHttpClientReturnsResponse(int statusCode, String response) {
208223
.willReturn(Future.succeededFuture(HttpClientResponse.of(statusCode, null, response)));
209224
}
210225

211-
private String givenLiveGvlJson(Map<Integer, String> vendorIdToDeletedDate) throws JsonProcessingException {
212-
final Map<Integer, Object> vendors = new HashMap<>();
213-
for (Map.Entry<Integer, String> entry : vendorIdToDeletedDate.entrySet()) {
214-
vendors.put(entry.getKey(), Map.of("id", entry.getKey(), "deletedDate", entry.getValue()));
215-
}
216-
return mapper.writeValueAsString(Map.of("vendors", vendors));
217-
}
218-
219226
private static Vendor givenVendor(int id, String deletedDate) {
220227
return Vendor.builder()
221228
.id(id)
222229
.deletedDate(deletedDate != null ? Instant.parse(deletedDate) : null)
230+
.purposes(EnumSet.of(ONE))
231+
.legIntPurposes(EnumSet.of(TWO))
232+
.flexiblePurposes(EnumSet.noneOf(PurposeCode.class))
233+
.specialPurposes(EnumSet.noneOf(SpecialPurpose.class))
234+
.features(EnumSet.noneOf(Feature.class))
235+
.specialFeatures(EnumSet.noneOf(SpecialFeature.class))
223236
.build();
224237
}
225238

226-
private static VendorList givenVendorList(Map<Integer, Vendor> vendors) {
227-
return VendorList.of(1, Date.from(Instant.parse("2020-08-20T16:05:24Z")), vendors);
239+
private static VendorList givenVendorList(Vendor... vendors) {
240+
return VendorList.of(
241+
1,
242+
Date.from(Instant.parse("2020-08-20T16:05:24Z")),
243+
Arrays.stream(vendors).collect(Collectors.toMap(Vendor::getId, Function.identity())));
228244
}
229245
}

0 commit comments

Comments
 (0)