Skip to content

Commit 409ebe0

Browse files
authored
Merge pull request #117 from Kentico/feature/custom-headers
Add posibility to add custom headers
2 parents 60b05cc + 8c6a10b commit 409ebe0

File tree

8 files changed

+183
-9
lines changed

8 files changed

+183
-9
lines changed

kontent-delivery/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ You can also provide the project ID and other parameters by passing the [`Delive
6363
- `setProductionEndpoint(String)` - sets the production endpoint address. Mainly useful to change for mocks in unit tests, or if you are establishing a proxy.
6464
- `setPreviewEndpoint(String)` - sets the preview endpoint address. Mainly useful to change for mocks in unit tests, or if you are establishing a proxy.
6565
- `setProxyServer(java.net.Proxy)` - sets the proxy server used by the http client. Mainly used to complex Proxy scenarios.
66+
- `setCustomHeaders(java.utils.List<kentico.kontent.delivery.Header>)` - sets custom headers to be included in the request. *Check the reserved header names in method remarks. These will be ignored.*
6667

6768
The `DeliveryOptions.builder()` can also simplify creating a `DeliveryClient`:
6869

kontent-delivery/src/main/java/kentico/kontent/delivery/DeliveryClient.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@
4747
@Slf4j
4848
public class DeliveryClient {
4949

50-
private static final String HEADER_X_KC_WAIT_FOR_LOADING_NEW_CONTENT = "X-KC-Wait-For-Loading-New-Content";
50+
public static final String HEADER_X_KC_WAIT_FOR_LOADING_NEW_CONTENT = "X-KC-Wait-For-Loading-New-Content";
51+
public static final String HEADER_X_KC_SDK_ID = "X-KC-SDKID";
52+
public static final String HEADER_AUTHORIZATION = "Authorization";
53+
public static final String HEADER_ACCEPT = "Accept";
54+
private static final String[] RESERVED_HEADERS = new String[]{HEADER_ACCEPT, HEADER_X_KC_SDK_ID, HEADER_AUTHORIZATION, HEADER_X_KC_WAIT_FOR_LOADING_NEW_CONTENT};
5155
private static String sdkId;
5256

5357
static {
@@ -360,7 +364,7 @@ public void registerInlineContentItemsResolver(InlineContentItemsResolver resolv
360364
/**
361365
* Not working on Android platform because of JVM and Dalvik differences, please use {@link DeliveryClient#registerType(Class)} instead
362366
* Register by scanning the classpath for annotated classes by {@link ContentItemMapping} annotation.
363-
*
367+
*
364368
* @param basePackage name of the base package
365369
*/
366370
@SuppressWarnings("WeakerAccess")
@@ -578,17 +582,28 @@ public void onResponse(Call call, Response response) throws IOException {
578582

579583
private Request buildNewRequest(String url) {
580584
Request.Builder requestBuilder = new Request.Builder().url(url);
581-
requestBuilder.header("Accept", "applications/json");
582-
requestBuilder.header("X-KC-SDKID", sdkId);
585+
requestBuilder.header(HEADER_ACCEPT, "applications/json");
586+
requestBuilder.header(HEADER_X_KC_SDK_ID, sdkId);
583587

584588
if (deliveryOptions.getProductionApiKey() != null) {
585-
requestBuilder.header("Authorization", String.format("Bearer %s", deliveryOptions.getProductionApiKey()));
589+
requestBuilder.header(HEADER_AUTHORIZATION, String.format("Bearer %s", deliveryOptions.getProductionApiKey()));
586590
} else if (deliveryOptions.isUsePreviewApi()) {
587-
requestBuilder.header("Authorization", String.format("Bearer %s", deliveryOptions.getPreviewApiKey()));
591+
requestBuilder.header(HEADER_AUTHORIZATION, String.format("Bearer %s", deliveryOptions.getPreviewApiKey()));
588592
}
589593
if (deliveryOptions.isWaitForLoadingNewContent()) {
590594
requestBuilder.header(HEADER_X_KC_WAIT_FOR_LOADING_NEW_CONTENT, "true");
591595
}
596+
597+
if (deliveryOptions.getCustomHeaders() != null){
598+
for (Header header : deliveryOptions.getCustomHeaders()) {
599+
if (Arrays.stream(RESERVED_HEADERS).anyMatch(header.getName()::equals)) {
600+
log.info("Custom header with name {} will be ignored", header.getName());
601+
} else {
602+
requestBuilder.header(header.getName(), header.getValue());
603+
}
604+
}
605+
}
606+
592607
return requestBuilder.build();
593608

594609
}

kontent-delivery/src/main/java/kentico/kontent/delivery/DeliveryOptions.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import lombok.Builder;
2828

2929
import java.net.Proxy;
30+
import java.util.List;
3031

3132
/**
3233
* Keeps settings which are provided by customer or have default values, used in {@link DeliveryClient}.
@@ -158,6 +159,12 @@ public class DeliveryOptions {
158159
@Builder.Default
159160
Proxy proxyServer = null;
160161

162+
/**
163+
* Include custom request headers. Headers with name {@link DeliveryClient#HEADER_ACCEPT}, {@link DeliveryClient#HEADER_AUTHORIZATION}, {@link DeliveryClient#HEADER_X_KC_SDK_ID}, {@link DeliveryClient#HEADER_X_KC_WAIT_FOR_LOADING_NEW_CONTENT} will be ignored.
164+
*/
165+
@Builder.Default
166+
List<Header> customHeaders = null;
167+
161168
/**
162169
* Constructs a setting instance of {@link DeliveryOptions} using your Kentico Kontent Project identifier.
163170
*
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package kentico.kontent.delivery;
2+
3+
@lombok.Data
4+
@lombok.AllArgsConstructor
5+
public class Header {
6+
7+
private String name;
8+
9+
private String value;
10+
11+
}

kontent-delivery/src/test/java/kentico/kontent/delivery/DeliveryClientTest.java

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import java.io.InputStreamReader;
4343
import java.net.URI;
4444
import java.nio.charset.Charset;
45+
import java.util.Arrays;
4546
import java.util.HashMap;
4647
import java.util.List;
4748
import java.util.Map;
@@ -85,6 +86,97 @@ public void testSdkIdHeader() throws Exception {
8586
Assert.assertNotNull(item);
8687
}
8788

89+
@Test
90+
public void testCustomHeadersPropagatedWithSdkIdHeader() throws Exception {
91+
String projectId = "02a70003-e864-464e-b62c-e0ede97deb8c";
92+
String previewApiKey = "preview_api_key";
93+
List<Header> headers = Arrays.asList(
94+
new Header("test-header-name1", "test-header-value1"),
95+
new Header("test-header-name2", "test-header-value2")
96+
);
97+
98+
this.serverBootstrap.registerHandler(
99+
String.format("/%s/%s", projectId, "items/on_roasts"),
100+
(request, response, context) -> {
101+
String[] trackingHeaderValueParts = request.getHeaders("X-KC-SDKID")[0].getValue().split(";");
102+
Assert.assertEquals(3, trackingHeaderValueParts.length);
103+
Assert.assertEquals("jCenter", trackingHeaderValueParts[0]);
104+
Assert.assertEquals("com.github.kentico:kontent-delivery", trackingHeaderValueParts[1]);
105+
Assert.assertTrue("Tracking header version value does not comply with semver definition.", trackingHeaderValueParts[2].matches(SEMVER_REGEX));
106+
107+
Assert.assertEquals(headers.get(0).getValue(), request.getHeaders(headers.get(0).getName())[0].getValue());
108+
Assert.assertEquals(headers.get(1).getValue(), request.getHeaders(headers.get(1).getName())[0].getValue());
109+
110+
response.setEntity(
111+
new InputStreamEntity(
112+
this.getClass().getResourceAsStream("SampleContentItem.json")
113+
)
114+
);
115+
});
116+
117+
HttpHost httpHost = this.start();
118+
DeliveryClient client = new DeliveryClient(
119+
DeliveryOptions
120+
.builder()
121+
.projectId(projectId)
122+
.previewApiKey(previewApiKey)
123+
.usePreviewApi(true)
124+
.customHeaders(headers)
125+
.build()
126+
);
127+
128+
String testServerUri = httpHost.toURI();
129+
client.getDeliveryOptions().setPreviewEndpoint(testServerUri);
130+
131+
ContentItemResponse item = client.getItem("on_roasts").toCompletableFuture().get();
132+
Assert.assertNotNull(item);
133+
}
134+
135+
@Test
136+
public void testCustomHeadersDoNotOverwriteReservedHeaders() throws Exception {
137+
String projectId = "02a70003-e864-464e-b62c-e0ede97deb8c";
138+
String previewApiKey = "preview_api_key";
139+
String customHeaderValue = "CUSTOM_VALUE_TO_BE_IGNORED";
140+
List<Header> headers = Arrays.asList(
141+
new Header(DeliveryClient.HEADER_AUTHORIZATION, customHeaderValue),
142+
new Header(DeliveryClient.HEADER_ACCEPT, customHeaderValue),
143+
new Header(DeliveryClient.HEADER_X_KC_SDK_ID, customHeaderValue),
144+
new Header(DeliveryClient.HEADER_X_KC_WAIT_FOR_LOADING_NEW_CONTENT, customHeaderValue)
145+
);
146+
147+
this.serverBootstrap.registerHandler(
148+
String.format("/%s/%s", projectId, "items/on_roasts"),
149+
(request, response, context) -> {
150+
Assert.assertNotEquals(headers.get(0).getValue(), request.getHeaders(headers.get(0).getName())[0].getValue());
151+
Assert.assertNotEquals(headers.get(1).getValue(), request.getHeaders(headers.get(1).getName())[0].getValue());
152+
Assert.assertNotEquals(headers.get(2).getValue(), request.getHeaders(headers.get(2).getName())[0].getValue());
153+
Assert.assertArrayEquals(new Header[] {}, request.getHeaders(headers.get(3).getName()));
154+
155+
response.setEntity(
156+
new InputStreamEntity(
157+
this.getClass().getResourceAsStream("SampleContentItem.json")
158+
)
159+
);
160+
});
161+
162+
HttpHost httpHost = this.start();
163+
DeliveryClient client = new DeliveryClient(
164+
DeliveryOptions
165+
.builder()
166+
.projectId(projectId)
167+
.previewApiKey(previewApiKey)
168+
.usePreviewApi(true)
169+
.customHeaders(headers)
170+
.build()
171+
);
172+
173+
String testServerUri = httpHost.toURI();
174+
client.getDeliveryOptions().setPreviewEndpoint(testServerUri);
175+
176+
ContentItemResponse item = client.getItem("on_roasts").toCompletableFuture().get();
177+
Assert.assertNotNull(item);
178+
}
179+
88180
@Test
89181
public void testRetryWorksForRetryStatusCode() throws Exception {
90182
String projectId = "02a70003-e864-464e-b62c-e0ede97deb8c";

sample-app-android-kotlin/src/main/java/kentico/kontent/delivery/sample/dancinggoat/data/DeliveryClientProvider.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,30 @@ package kentico.kontent.delivery.sample.dancinggoat.data
22

33
import kentico.kontent.delivery.DeliveryClient
44
import kentico.kontent.delivery.DeliveryOptions
5+
import kentico.kontent.delivery.Header
56
import kentico.kontent.delivery.sample.dancinggoat.models.Article
7+
import java.util.*
68

79
object DeliveryClientProvider {
10+
// https://github.com/Kentico/Home/wiki/Guidelines-for-Kontent-related-tools#analytics
11+
private const val TRACKING_HEADER_NAME = "X-KC-SOURCE"
12+
private const val TRACKING_HEADER_VALUE = "kentico.kontent.delivery.sample.dancinggoat.android.kotlin;1.0.0"
13+
814
private var instance: DeliveryClient? = null
915

1016
val client: DeliveryClient
1117
get() {
1218
if (instance == null) {
13-
var client = DeliveryClient(DeliveryOptions("975bf280-fd91-488c-994c-2f04416e5ee3"), null)
19+
val client = DeliveryClient(
20+
DeliveryOptions
21+
.builder()
22+
.projectId("975bf280-fd91-488c-994c-2f04416e5ee3")
23+
.customHeaders(listOf(
24+
Header(TRACKING_HEADER_NAME, TRACKING_HEADER_VALUE)
25+
))
26+
.build(),
27+
null
28+
)
1429
client.registerType(Article::class.java)
1530
instance = client
1631
}

sample-app-android/src/main/java/com/github/kentico/delivery_android_sample/data/source/DeliveryClientProvider.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,29 @@
66
import com.github.kentico.delivery_android_sample.data.models.Coffee;
77
import kentico.kontent.delivery.DeliveryClient;
88
import kentico.kontent.delivery.DeliveryOptions;
9+
import kentico.kontent.delivery.Header;
10+
11+
import java.util.Arrays;
912

1013
public class DeliveryClientProvider {
14+
// https://github.com/Kentico/Home/wiki/Guidelines-for-Kontent-related-tools#analytics
15+
private static final String TRACKING_HEADER_NAME = "X-KC-SOURCE";
16+
private static final String TRACKING_HEADER_VALUE = "com.github.kentico.delivery_android_sample;1.0.0";
17+
1118
private static DeliveryClient INSTANCE;
1219

1320
public static DeliveryClient getClient() {
1421
if (INSTANCE == null) {
15-
DeliveryClient client = new DeliveryClient(new DeliveryOptions(AppConfig.KONTENT_PROJECT_ID), null);
22+
DeliveryClient client = new DeliveryClient(
23+
DeliveryOptions
24+
.builder()
25+
.projectId(AppConfig.KONTENT_PROJECT_ID)
26+
.customHeaders(Arrays.asList(
27+
new Header(TRACKING_HEADER_NAME, TRACKING_HEADER_VALUE)
28+
))
29+
.build(),
30+
null
31+
);
1632
client.registerType(Article.class);
1733
client.registerType(Cafe.class);
1834
client.registerType(Coffee.class);

sample-app-spring-boot/src/main/java/kentico/kontent/delivery/sample/dancinggoat/springboot/KontentConfiguration.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,37 @@
11
package kentico.kontent.delivery.sample.dancinggoat.springboot;
22

33
import kentico.kontent.delivery.DeliveryClient;
4+
import kentico.kontent.delivery.DeliveryOptions;
5+
import kentico.kontent.delivery.Header;
46
import kentico.kontent.delivery.InlineContentItemsResolver;
57
import kentico.kontent.delivery.sample.dancinggoat.models.Tweet;
68
import org.springframework.boot.json.JsonParserFactory;
79
import org.springframework.context.annotation.Bean;
810
import org.springframework.context.annotation.Configuration;
911
import org.springframework.web.client.RestTemplate;
1012

13+
import java.util.Arrays;
1114
import java.util.Map;
1215

1316
@Configuration
1417
public class KontentConfiguration {
18+
19+
// https://github.com/Kentico/Home/wiki/Guidelines-for-Kontent-related-tools#analytics
20+
private static final String TRACKING_HEADER_NAME = "X-KC-SOURCE";
21+
private static final String TRACKING_HEADER_VALUE = "kentico.kontent.delivery.sample.dancinggoat.java.springboot;1.0.0";
22+
1523
@Bean
1624
public DeliveryClient deliveryClient() {
17-
DeliveryClient client = new DeliveryClient("975bf280-fd91-488c-994c-2f04416e5ee3");
25+
DeliveryClient client = new DeliveryClient(
26+
DeliveryOptions
27+
.builder()
28+
.projectId("975bf280-fd91-488c-994c-2f04416e5ee3")
29+
.customHeaders(Arrays.asList(
30+
new Header(TRACKING_HEADER_NAME, TRACKING_HEADER_VALUE)
31+
))
32+
.build()
33+
);
34+
1835
client.scanClasspathForMappings("kentico.kontent.delivery.sample.dancinggoat.models");
1936

2037
client.registerInlineContentItemsResolver(new InlineContentItemsResolver<Tweet>() {

0 commit comments

Comments
 (0)