Skip to content

Commit 73057dd

Browse files
authored
Merge pull request #80 from gldiazcardenas/master
Issue-79 Added contextual targeting support
2 parents e2f15bf + 4acbc48 commit 73057dd

File tree

10 files changed

+298
-0
lines changed

10 files changed

+298
-0
lines changed

src/main/java/com/taboola/backstage/internal/BackstageAccountEndpoint.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.taboola.backstage.model.Results;
55
import com.taboola.backstage.model.Account;
66
import com.taboola.backstage.model.dictionary.AudienceSegment;
7+
import com.taboola.backstage.model.dictionary.ContextualSegment;
78
import com.taboola.backstage.model.dictionary.LookalikeAudience;
89

910
import com.taboola.backstage.model.media.account.AccountBlockedPublishersPatch;
@@ -16,6 +17,7 @@
1617
import retrofit2.http.PATCH;
1718
import retrofit2.http.POST;
1819
import retrofit2.http.Path;
20+
import retrofit2.http.Query;
1921

2022
/**
2123
* Created by vladi
@@ -48,6 +50,12 @@ Results<AudienceSegment> getSpecificCountryAudienceSegments(@Header("Authorizati
4850
@Path("account_id") String accountId,
4951
@Path("country_code") String countryCode) throws BackstageAPIException;
5052

53+
@GET(BackstagePaths.BACKSTAGE_API_PATH_PREFIX + "/{account_id}/dictionary/contextual_segments")
54+
@Headers("Content-Type: application/json")
55+
Results<ContextualSegment> getContextualSegments(@Header("Authorization") String accessToken,
56+
@Path("account_id") String accountId,
57+
@Query("countryCodes") String countryCodes) throws BackstageAPIException;
58+
5159
@GET(BackstagePaths.BACKSTAGE_API_PATH_PREFIX + "/{account_id}/dictionary/lookalike_audiences")
5260
@Headers("Content-Type: application/json")
5361
Results<LookalikeAudience> getLookalikeAudiences(@Header("Authorization") String accessToken,

src/main/java/com/taboola/backstage/internal/BackstageAudienceTargetingEndpoint.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ CampaignMultiTargetingCollection<Long> readAudienceSegments(@Header("Authorizati
3232
@Path("account_id") String accountId,
3333
@Path("campaign_id") String campaignId) throws BackstageAPIException;
3434

35+
@POST(BackstagePaths.BACKSTAGE_API_PATH_PREFIX + "/{account_id}/campaigns/{campaign_id}/targeting/contextual_segments")
36+
@Headers("Content-Type: application/json")
37+
CampaignMultiTargetingCollection<Long> updateContextualSegments(@Header("Authorization") String accessToken,
38+
@Path("account_id") String accountId,
39+
@Path("campaign_id") String campaignId,
40+
@Body CampaignMultiTargetingCollection<Long> targeting) throws BackstageAPIException;
41+
42+
@GET(BackstagePaths.BACKSTAGE_API_PATH_PREFIX + "/{account_id}/campaigns/{campaign_id}/targeting/contextual_segments")
43+
@Headers("Content-Type: application/json")
44+
CampaignMultiTargetingCollection<Long> readContextualSegments(@Header("Authorization") String authToken,
45+
@Path("account_id") String accountId,
46+
@Path("campaign_id") String campaignId) throws BackstageAPIException;
47+
3548
@POST(BackstagePaths.BACKSTAGE_API_PATH_PREFIX + "/{account_id}/campaigns/{campaign_id}/targeting/custom_audience")
3649
@Headers("Content-Type: application/json")
3750
CampaignMultiTargetingCollection<Long> updateCustomAudience(@Header("Authorization") String accessToken,
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package com.taboola.backstage.model.dictionary;
2+
3+
/**
4+
* @author Gabriel Diaz, 21/11/23.
5+
*/
6+
public class ContextualSegment {
7+
8+
private Long id;
9+
private String provider;
10+
private String label;
11+
private String description;
12+
private String taxonomy;
13+
private String allowedCountries;
14+
private Long size;
15+
private Long articles;
16+
private Boolean isTargetable;
17+
private Boolean isBundle;
18+
private Boolean isActive;
19+
private String taxonomyLabel;
20+
21+
public Long getId() {
22+
return id;
23+
}
24+
25+
public void setId(Long id) {
26+
this.id = id;
27+
}
28+
29+
public String getProvider() {
30+
return provider;
31+
}
32+
33+
public void setProvider(String provider) {
34+
this.provider = provider;
35+
}
36+
37+
public String getLabel() {
38+
return label;
39+
}
40+
41+
public void setLabel(String label) {
42+
this.label = label;
43+
}
44+
45+
public String getDescription() {
46+
return description;
47+
}
48+
49+
public void setDescription(String description) {
50+
this.description = description;
51+
}
52+
53+
public String getTaxonomy() {
54+
return taxonomy;
55+
}
56+
57+
public void setTaxonomy(String taxonomy) {
58+
this.taxonomy = taxonomy;
59+
}
60+
61+
public String getAllowedCountries() {
62+
return allowedCountries;
63+
}
64+
65+
public void setAllowedCountries(String allowedCountries) {
66+
this.allowedCountries = allowedCountries;
67+
}
68+
69+
public Long getSize() {
70+
return size;
71+
}
72+
73+
public void setSize(Long size) {
74+
this.size = size;
75+
}
76+
77+
public Long getArticles() {
78+
return articles;
79+
}
80+
81+
public void setArticles(Long articles) {
82+
this.articles = articles;
83+
}
84+
85+
private Boolean getIsTargetable() {
86+
return isTargetable;
87+
}
88+
89+
public void setIsTargetable(Boolean isTargetable) {
90+
this.isTargetable = isTargetable;
91+
}
92+
93+
public Boolean getIsBundle() {
94+
return isBundle;
95+
}
96+
97+
public void setIsBundle(Boolean isBundle) {
98+
this.isBundle = isBundle;
99+
}
100+
101+
public Boolean getIsActive() {
102+
return isActive;
103+
}
104+
105+
public void setIsActive(Boolean isActive) {
106+
this.isActive = isActive;
107+
}
108+
109+
public String getTaxonomyLabel() {
110+
return taxonomyLabel;
111+
}
112+
113+
public void setTaxonomyLabel(String taxonomyLabel) {
114+
this.taxonomyLabel = taxonomyLabel;
115+
}
116+
117+
@Override
118+
public String toString() {
119+
return "ContextualSegment{" +
120+
"id=" + id +
121+
", provider='" + provider + '\'' +
122+
", label='" + label + '\'' +
123+
", description='" + description + '\'' +
124+
", taxonomy='" + taxonomy + '\'' +
125+
", allowedCountries='" + allowedCountries + '\'' +
126+
", size=" + size +
127+
", articles=" + articles +
128+
", isTargetable=" + isTargetable +
129+
", isBundle=" + isBundle +
130+
", isActive=" + isActive +
131+
", taxonomyLabel='" + taxonomyLabel + '\'' +
132+
'}';
133+
}
134+
135+
}

src/main/java/com/taboola/backstage/services/AccountsService.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.taboola.backstage.model.Results;
88
import com.taboola.backstage.model.auth.BackstageAuthentication;
99
import com.taboola.backstage.model.dictionary.AudienceSegment;
10+
import com.taboola.backstage.model.dictionary.ContextualSegment;
1011
import com.taboola.backstage.model.dictionary.LookalikeAudience;
1112
import com.taboola.backstage.model.media.account.AccountBlockedPublishers;
1213
import com.taboola.backstage.model.media.account.AccountBlockedPublishersPatch;
@@ -54,6 +55,31 @@ public interface AccountsService {
5455
*/
5556
Results<AudienceSegment> readAudienceSegments(BackstageAuthentication auth, String accountId, String countryCode) throws BackstageAPIUnauthorizedException, BackstageAPIConnectivityException, BackstageAPIRequestException;
5657

58+
/**
59+
* Read contextual segments.
60+
*
61+
* @param auth Authentication object {@link BackstageAuthentication}
62+
* @param accountId {@link com.taboola.backstage.model.Account Account} to which {@link AudienceSegment} belongs. Taken from {@link com.taboola.backstage.model.Account#getAccountId Account.getAccountId()}
63+
* @return Fully populated collection of {@link ContextualSegment}
64+
* @throws BackstageAPIUnauthorizedException {@link com.taboola.backstage.model.auth.Token Token} is expired or bad credentials
65+
* @throws BackstageAPIConnectivityException Connectivity issues (HTTP status 5xx)
66+
* @throws BackstageAPIRequestException Bad request (HTTP status 4xx)
67+
*/
68+
Results<ContextualSegment> readContextualSegments(BackstageAuthentication auth, String accountId) throws BackstageAPIUnauthorizedException, BackstageAPIConnectivityException, BackstageAPIRequestException;
69+
70+
/**
71+
* Read contextual segments given a list of country codes (codes supplied by {@link DictionaryService#getCountries})
72+
*
73+
* @param auth Authentication object {@link BackstageAuthentication}
74+
* @param accountId {@link com.taboola.backstage.model.Account Account} to which {@link AudienceSegment} belongs. Taken from {@link com.taboola.backstage.model.Account#getAccountId Account.getAccountId()}
75+
* @param countryCodes taken from {@link DictionaryService#getCountries}
76+
* @return Fully populated collection of {@link ContextualSegment}
77+
* @throws BackstageAPIUnauthorizedException {@link com.taboola.backstage.model.auth.Token Token} is expired or bad credentials
78+
* @throws BackstageAPIConnectivityException Connectivity issues (HTTP status 5xx)
79+
* @throws BackstageAPIRequestException Bad request (HTTP status 4xx)
80+
*/
81+
Results<ContextualSegment> readContextualSegments(BackstageAuthentication auth, String accountId, String... countryCodes) throws BackstageAPIUnauthorizedException, BackstageAPIConnectivityException, BackstageAPIRequestException;
82+
5783
/**
5884
* Read all lookalike audiences
5985
*

src/main/java/com/taboola/backstage/services/AccountsServiceImpl.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.taboola.backstage.model.Results;
99
import com.taboola.backstage.model.auth.BackstageAuthentication;
1010
import com.taboola.backstage.model.dictionary.AudienceSegment;
11+
import com.taboola.backstage.model.dictionary.ContextualSegment;
1112
import com.taboola.backstage.model.dictionary.LookalikeAudience;
1213
import com.taboola.backstage.model.media.account.AccountBlockedPublishersPatch;
1314
import com.taboola.backstage.model.media.account.AccountBlockedPublishers;
@@ -47,6 +48,18 @@ public Results<AudienceSegment> readAudienceSegments(BackstageAuthentication aut
4748
return endpoint.getSpecificCountryAudienceSegments(accessToken, accountId, countryCode);
4849
}
4950

51+
@Override
52+
public Results<ContextualSegment> readContextualSegments(BackstageAuthentication auth, String accountId) throws BackstageAPIUnauthorizedException, BackstageAPIConnectivityException, BackstageAPIRequestException {
53+
String accessToken = auth.getToken().getAccessTokenForHeader();
54+
return endpoint.getContextualSegments(accessToken, accountId, null);
55+
}
56+
57+
@Override
58+
public Results<ContextualSegment> readContextualSegments(BackstageAuthentication auth, String accountId, String... countryCodes) throws BackstageAPIUnauthorizedException, BackstageAPIConnectivityException, BackstageAPIRequestException {
59+
String accessToken = auth.getToken().getAccessTokenForHeader();
60+
return endpoint.getContextualSegments(accessToken, accountId, String.join(",", countryCodes));
61+
}
62+
5063
@Override
5164
public Results<LookalikeAudience> readLookalikeAudiences(BackstageAuthentication auth, String accountId) throws BackstageAPIUnauthorizedException, BackstageAPIConnectivityException, BackstageAPIRequestException {
5265
String accessToken = auth.getToken().getAccessTokenForHeader();

src/main/java/com/taboola/backstage/services/CampaignAudienceTargetingService.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,33 @@ public interface CampaignAudienceTargetingService {
4242
*/
4343
CampaignMultiTargetingCollection<Long> updateAudienceSegments(BackstageAuthentication auth, String accountId, String campaignId, CampaignMultiTargetingCollection<Long> targeting) throws BackstageAPIUnauthorizedException, BackstageAPIConnectivityException, BackstageAPIRequestException;
4444

45+
/**
46+
* Read contextual segments targeting.
47+
*
48+
* @param auth Authentication object ({@link BackstageAuthentication})
49+
* @param accountId To which {@link com.taboola.backstage.model.Account Account} the campaign belongs. Taken from {@link com.taboola.backstage.model.Account#getAccountId() Account.getAccountId()}
50+
* @param campaignId {@link com.taboola.backstage.model.media.campaigns.Campaign Campaign} Id. Taken from {@link com.taboola.backstage.model.media.campaigns.Campaign Campaign#getId()} object
51+
* @return Fully populated {@link CampaignMultiTargetingCollection}
52+
* @throws BackstageAPIUnauthorizedException {@link com.taboola.backstage.model.auth.Token Token} is expired or bad credentials
53+
* @throws BackstageAPIConnectivityException Connectivity issues (HTTP status 5xx)
54+
* @throws BackstageAPIRequestException Bad request (HTTP status 4xx)
55+
*/
56+
CampaignMultiTargetingCollection<Long> readContextualSegments(BackstageAuthentication auth, String accountId, String campaignId) throws BackstageAPIUnauthorizedException, BackstageAPIConnectivityException, BackstageAPIRequestException;
57+
58+
/**
59+
* Updating contextual segments targeting.
60+
*
61+
* @param auth Authentication object ({@link BackstageAuthentication})
62+
* @param accountId To which {@link com.taboola.backstage.model.Account Account} the campaign belongs. Taken from {@link com.taboola.backstage.model.Account#getAccountId() Account.getAccountId()}
63+
* @param campaignId {@link com.taboola.backstage.model.media.campaigns.Campaign Campaign} Id. Taken from {@link com.taboola.backstage.model.media.campaigns.Campaign Campaign#getId()} object
64+
* @param targeting Full or partial {@link CampaignMultiTargetingCollection} pojo to update.
65+
* @return Fully populated {@link CampaignMultiTargetingCollection}
66+
* @throws BackstageAPIUnauthorizedException {@link com.taboola.backstage.model.auth.Token Token} is expired or bad credentials
67+
* @throws BackstageAPIConnectivityException Connectivity issues (HTTP status 5xx)
68+
* @throws BackstageAPIRequestException Bad request (HTTP status 4xx)
69+
*/
70+
CampaignMultiTargetingCollection<Long> updateContextualSegments(BackstageAuthentication auth, String accountId, String campaignId, CampaignMultiTargetingCollection<Long> targeting) throws BackstageAPIUnauthorizedException, BackstageAPIConnectivityException, BackstageAPIRequestException;
71+
4572
/**
4673
* Read custom audience targeting
4774
*

src/main/java/com/taboola/backstage/services/CampaignAudienceTargetingServiceImpl.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ public CampaignMultiTargetingCollection<Long> updateAudienceSegments(BackstageAu
3939
return endpoint.updateAudienceSegments(auth.getToken().getAccessTokenForHeader(), accountId, campaignId, targeting);
4040
}
4141

42+
@Override
43+
public CampaignMultiTargetingCollection<Long> readContextualSegments(BackstageAuthentication auth, String accountId, String campaignId) throws BackstageAPIUnauthorizedException, BackstageAPIConnectivityException, BackstageAPIRequestException {
44+
return endpoint.readContextualSegments(auth.getToken().getAccessTokenForHeader(), accountId, campaignId);
45+
}
46+
47+
@Override
48+
public CampaignMultiTargetingCollection<Long> updateContextualSegments(BackstageAuthentication auth, String accountId, String campaignId, CampaignMultiTargetingCollection<Long> targeting) throws BackstageAPIUnauthorizedException, BackstageAPIConnectivityException, BackstageAPIRequestException {
49+
if(performClientValidations) {
50+
FieldsValidator.validateCreateOperation(targeting);
51+
}
52+
return endpoint.updateContextualSegments(auth.getToken().getAccessTokenForHeader(), accountId, campaignId, targeting);
53+
}
54+
4255
@Override
4356
public CampaignMultiTargetingCollection<Long> readCustomAudience(BackstageAuthentication auth, String accountId, String campaignId) throws BackstageAPIUnauthorizedException, BackstageAPIConnectivityException, BackstageAPIRequestException {
4457
return endpoint.readCustomAudience(auth.getToken().getAccessTokenForHeader(), accountId, campaignId);

src/test/java/com/taboola/backstage/BackstageTestBase.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import com.taboola.backstage.model.auth.PasswordAuthenticationDetails;
1414
import com.taboola.backstage.model.auth.Token;
1515
import com.taboola.backstage.model.dictionary.AudienceSegment;
16+
import com.taboola.backstage.model.dictionary.ContextualSegment;
1617
import com.taboola.backstage.model.dictionary.LookalikeAudience;
1718
import com.taboola.backstage.model.dictionary.Resource;
1819
import com.taboola.backstage.model.dynamic.DynamicField;
@@ -209,6 +210,10 @@ protected CampaignMultiTargetingCollection<Long> generateDummyAudienceMultiTarge
209210
return factory.manufacturePojo(CampaignMultiTargetingCollection.class, Long.class.getGenericSuperclass());
210211
}
211212

213+
protected CampaignMultiTargetingCollection<Long> generateDummyContextualMultiTargeting() {
214+
return factory.manufacturePojo(CampaignMultiTargetingCollection.class, Long.class.getGenericSuperclass());
215+
}
216+
212217
protected CampaignMultiTargetingCollection<Long> generateDummyCustomAudience() {
213218
return factory.manufacturePojo(CampaignMultiTargetingCollection.class, Long.class.getGenericSuperclass());
214219
}
@@ -263,6 +268,10 @@ protected AudienceSegment generateDummyAudienceSegment() {
263268
return factory.manufacturePojo(AudienceSegment.class);
264269
}
265270

271+
protected ContextualSegment generateDummyContextualSegment() {
272+
return factory.manufacturePojo(ContextualSegment.class);
273+
}
274+
266275
protected LookalikeAudience generateDummyLookalikeAudiences() {
267276
return factory.manufacturePojo(LookalikeAudience.class);
268277
}

src/test/java/com/taboola/backstage/services/AccountsServiceImplTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.taboola.backstage.model.Account;
55
import com.taboola.backstage.model.Results;
66
import com.taboola.backstage.model.auth.BackstageAuthentication;
7+
import com.taboola.backstage.model.dictionary.ContextualSegment;
78
import com.taboola.backstage.model.media.account.AccountBlockedPublishers;
89
import org.junit.Before;
910
import org.junit.Test;
@@ -70,6 +71,31 @@ public void testReadAudienceSegmentsByCountry(){
7071
assertEquals("Invalid accounts", expected, actual);
7172
}
7273

74+
@Test
75+
public void testReadContextualSegments(){
76+
String accountId = "123";
77+
ContextualSegment contextualSegment = generateDummyContextualSegment();
78+
Results<ContextualSegment> expected = new Results<>(Collections.singleton(contextualSegment));
79+
when(accountsEndpointMock.getContextualSegments(any(), eq(accountId), isNull(String.class))).thenReturn(expected);
80+
81+
BackstageAuthentication auth = generateDummyClientCredentialsBackstageAuth();
82+
Results<ContextualSegment> actual = testInstance.readContextualSegments(auth, accountId);
83+
assertEquals("Invalid contextual segments", expected, actual);
84+
}
85+
86+
@Test
87+
public void testReadContextualSegmentsByCountry(){
88+
String countryCode = "US";
89+
String accountId = "123";
90+
ContextualSegment contextualSegment = generateDummyContextualSegment();
91+
Results<ContextualSegment> expected = new Results<>(Collections.singleton(contextualSegment));
92+
when(accountsEndpointMock.getContextualSegments(any(), eq(accountId), eq(countryCode))).thenReturn(expected);
93+
94+
BackstageAuthentication auth = generateDummyClientCredentialsBackstageAuth();
95+
Results<ContextualSegment> actual = testInstance.readContextualSegments(auth, accountId, countryCode);
96+
assertEquals("Invalid contextual segments", expected, actual);
97+
}
98+
7399
@Test
74100
public void testReadLookalikeAudiences(){
75101
LookalikeAudience lookalikeAudience = generateDummyLookalikeAudiences();

0 commit comments

Comments
 (0)