Skip to content

Commit 5fcd5ed

Browse files
authored
Set header indicating product origin (#197)
* 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 8c53338 commit 5fcd5ed

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,6 +1,9 @@
11
## UNRELEASED
22
- Releases from `main` are not desired until we get closer to Stack 9.x, and beginning the changelog with something other than release notes matching the version will prevent the plugin release tooling from allowing publication.
33

4+
## 0.1.17
5+
- Add `x-elastic-product-origin` header to Elasticsearch requests [#197](https://github.com/elastic/logstash-filter-elastic_integration/pull/197)
6+
47
## 0.1.16
58
- 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)
69

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)