Skip to content

Commit dd9b699

Browse files
authored
Set header indicating product origin (#197) (#198)
* Set header indicating product origin This commit updates the elasticsearch client to set a header indicating which product component is using the API. Implementation note: The implementation follows the pattern set with setting the elastic API version header. The use of static factory methods for that make the test setup fairly convoluted. I think (with the help of copilot) i've figured out a way to mock everything, but it feels fairly complex. * Release prep for version 0.1.17
1 parent 55f3d32 commit dd9b699

File tree

5 files changed

+68
-2
lines changed

5 files changed

+68
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 0.1.17
2+
- Add `x-elastic-product-origin` header to Elasticsearch requests [#197](https://github.com/elastic/logstash-filter-elastic_integration/pull/197)
3+
14
## 0.1.16
25
- Reflects the Elasticsearch GeoIP changes into the plugin and syncs with Elasticsearch 8.16 branch [#170](https://github.com/elastic/logstash-filter-elastic_integration/pull/170)
36

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.16
1+
0.1.17

build.gradle

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ configurations {
5151

5252
geolite2 { canBeConsumed = false }
5353

54+
mockitoAgent { canBeConsumed = false }
55+
5456
implementation.extendsFrom(logstashCore, elasticsearchMinimalCore, elasticsearchClient)
5557
}
5658

@@ -93,6 +95,9 @@ dependencies {
9395
from requiredLogstashCoreJar("guava", "jre")
9496
})
9597

98+
mockitoAgent('org.mockito:mockito-core:5.14.1') {
99+
transitive = false
100+
}
96101
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.1.0'
97102
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.1.0'
98103
testImplementation 'org.mockito:mockito-junit-jupiter:5.14.1'
@@ -440,7 +445,11 @@ tasks.withType(Test) {
440445
"--add-opens=java.base/sun.nio.ch=ALL-UNNAMED",
441446
"--add-opens=java.base/java.io=ALL-UNNAMED",
442447
"--add-opens=java.base/java.lang=ALL-UNNAMED",
443-
"--add-opens=java.base/java.util=ALL-UNNAMED"
448+
"--add-opens=java.base/java.util=ALL-UNNAMED",
449+
"-Djdk.attach.allowAttachSelf=true",
450+
"-Dmockito.mock.maker=inline",
451+
"-javaagent:${configurations.mockitoAgent.asPath}",
452+
"-XX:+EnableDynamicAgentLoading"
444453
]
445454
}
446455

src/main/java/co/elastic/logstash/filters/elasticintegration/ElasticsearchRestClientBuilder.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,10 @@ public void configureHttpClient(final HttpAsyncClientBuilder httpClientBuilder)
461461
final HttpRequestInterceptor interceptor = new EAVHttpRequestInterceptor(elasticApiVersionHeader);
462462
HttpClientConfigurator.forAddInterceptorFirst(interceptor).configure(httpClientBuilder);
463463
}
464+
// Set header indicating internal use
465+
final BasicHeader productOriginHeader = new BasicHeader("x-elastic-product-origin", "logstash-filter-elastic_integration");
466+
final HttpRequestInterceptor productOriginHeaderInterceptor = new EAVHttpRequestInterceptor(productOriginHeader);
467+
HttpClientConfigurator.forAddInterceptorFirst(productOriginHeaderInterceptor).configure(httpClientBuilder);
464468
}
465469
}
466470
}

src/test/java/co/elastic/logstash/filters/elasticintegration/ElasticsearchRestClientBuilderTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@
66
*/
77
package co.elastic.logstash.filters.elasticintegration;
88

9+
import org.apache.http.HttpException;
910
import org.apache.http.HttpHost;
11+
import org.apache.http.HttpRequest;
12+
import org.apache.http.HttpRequestInterceptor;
13+
import org.apache.http.message.BasicHeader;
14+
import org.apache.http.message.BasicHttpRequest;
15+
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
1016
import org.elasticsearch.client.RestClient;
1117
import org.elasticsearch.client.RestClientBuilder;
1218
import org.hamcrest.Matchers;
@@ -18,11 +24,20 @@
1824
import java.util.Collection;
1925
import java.util.Collections;
2026
import java.util.List;
27+
import java.util.Objects;
28+
29+
import co.elastic.logstash.filters.elasticintegration.ElasticsearchRestClientBuilder.ElasticApiConfig;
30+
31+
import org.mockito.MockedStatic;
32+
import org.mockito.ArgumentCaptor;
2133

2234
import static org.hamcrest.MatcherAssert.assertThat;
2335
import static org.hamcrest.Matchers.containsString;
36+
import static org.hamcrest.Matchers.equalTo;
37+
import static org.hamcrest.Matchers.is;
2438
import static org.hamcrest.Matchers.stringContainsInOrder;
2539
import static org.junit.jupiter.api.Assertions.assertThrows;
40+
import static org.mockito.ArgumentCaptor.forClass;
2641
import static org.mockito.Mockito.*;
2742

2843
class ElasticsearchRestClientBuilderTest {
@@ -111,6 +126,41 @@ public void testForHostsFactoryURLsWithoutPort() {
111126
assertThat(illegalStateException.getMessage(), containsString("URLS must include port specification"));
112127
}
113128

129+
@Test
130+
public void testElasticApiConfigAddsHeaders() throws HttpException, IOException {
131+
ElasticApiConfig config = new ElasticApiConfig();
132+
config.setApiVersion("2023-10-31");
133+
134+
ElasticsearchRestClientBuilder.HttpClientConfigurator mockConfigurator =
135+
mock(ElasticsearchRestClientBuilder.HttpClientConfigurator.class);
136+
137+
try (MockedStatic<ElasticsearchRestClientBuilder.HttpClientConfigurator> mockedStatic =
138+
mockStatic(ElasticsearchRestClientBuilder.HttpClientConfigurator.class)) {
139+
mockedStatic.when(() ->
140+
ElasticsearchRestClientBuilder.HttpClientConfigurator.forAddInterceptorFirst(
141+
any(HttpRequestInterceptor.class)))
142+
.thenReturn(mockConfigurator);
143+
144+
HttpAsyncClientBuilder builder = HttpAsyncClientBuilder.create();
145+
config.configureHttpClient(builder);
146+
147+
ArgumentCaptor<HttpRequestInterceptor> interceptorCaptor = forClass(HttpRequestInterceptor.class);
148+
mockedStatic.verify(() ->
149+
ElasticsearchRestClientBuilder.HttpClientConfigurator.forAddInterceptorFirst(
150+
interceptorCaptor.capture()), times(2));
151+
verify(mockConfigurator, times(2)).configure(builder);
152+
153+
// Process interceptors and look for product origin header
154+
List<HttpRequestInterceptor> interceptors = interceptorCaptor.getAllValues();
155+
HttpRequest request = new BasicHttpRequest("GET", "/");
156+
for (HttpRequestInterceptor interceptor : interceptors) {
157+
interceptor.process(request, null);
158+
}
159+
assertThat(request.getFirstHeader("x-elastic-product-origin").getValue(),
160+
is("logstash-filter-elastic_integration"));
161+
}
162+
}
163+
114164
private <T> void validateTranslationToClientBuilderFactory(final Collection<URL> inputUrls, final HttpHost[] expectedInputReceivedByBuilderFactory) {
115165
final ElasticsearchRestClientBuilder.HostsArrayRestClientBuilderFactory hostsArrayRestClientBuilderFactory = spy(HOSTS_ARRAY_REST_CLIENT_BUILDER_FACTORY);
116166
try (RestClient restClient = ElasticsearchRestClientBuilder.forURLs(inputUrls, hostsArrayRestClientBuilderFactory).build()) {

0 commit comments

Comments
 (0)