Skip to content

Commit

Permalink
[283] Add new RestClientBuilder method for adding headers.
Browse files Browse the repository at this point in the history
Signed-off-by: Adam Anderson <[email protected]>
  • Loading branch information
WhiteCat22 committed Jun 13, 2024
1 parent 78693d7 commit b16fe10
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,20 @@ default RestClientBuilder baseUri(String uri) {
*/
RestClientBuilder queryParamStyle(QueryParamStyle style);

/**
* Add an arbitrary header.
*
* @param name
* - the name of the header
* @param name
* - the value of the HTTP header to add to the request.
* @return the current builder with the header added to the request.
* @throws NullPointerException
* if the value is null.
* @since 4.0
*/
RestClientBuilder header(String name, Object value);

/**
* Based on the configured RestClientBuilder, creates a new instance of the given REST interface to invoke API calls
* against.
Expand Down
13 changes: 13 additions & 0 deletions spec/src/main/asciidoc/clientexamples.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,19 @@ implementation must invoke the `DefaultClientHeadersFactoryImpl`. This default f

`org.eclipse.microprofile.rest.client.propagateHeaders`

You can also configure headers on a per instance basis using the `RestClientBuilder.header(String name, Object value)` method. Headers added via this method will be merged with the headers added via `@ClientHeaderParam` annotations, `@HeaderParam` annotations, and `ClientHeadersFactory` implementations.
**Note: The method will throw a `NullPointerException` if the value is `null`.**

Example:

[source, java]
----
RedirectClient client = RestClientBuilder.newBuilder()
.baseUri(someUri)
.header("Some-Header", headerValueObj)
.build(SomeClient.class);
----

=== Following Redirect Responses

By default, a Rest Client instance will not automatically follow redirect responses. Redirect responses are typically responses with status codes in the 300 range and include `Location` header that indicates the URL of the redirected resource.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright 2024 Contributors to the Eclipse Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.eclipse.microprofile.rest.client.tck;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.containing;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;

import java.util.Arrays;
import java.util.stream.Collectors;

import org.eclipse.microprofile.rest.client.RestClientBuilder;
import org.eclipse.microprofile.rest.client.tck.interfaces.SimpleGetApi;
import org.eclipse.microprofile.rest.client.tck.providers.ReturnWithAllClientHeadersFilter;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import com.github.tomakehurst.wiremock.client.MappingBuilder;

public class ClientBuilderHeaderTest extends WiremockArquillianTest {
@Deployment
public static Archive<?> createDeployment() {
return ShrinkWrap.create(WebArchive.class, ClientBuilderHeaderTest.class.getSimpleName() + ".war")
.addClasses(
SimpleGetApi.class,
ReturnWithAllClientHeadersFilter.class,
WiremockArquillianTest.class);
}

private static void stub(String expectedHeaderName, String... expectedHeaderValue) {
String expectedIncomingHeader = Arrays.stream(expectedHeaderValue)
.collect(Collectors.joining(","));
String outputBody = expectedIncomingHeader.replace(',', '-');
MappingBuilder mappingBuilder = get(urlEqualTo("/"));

// headers can be sent either in a single line with comma-separated values or in multiple lines
// this should match both cases:
Arrays.stream(expectedHeaderValue)
.forEach(val -> mappingBuilder.withHeader(expectedHeaderName, containing(val)));
stubFor(
mappingBuilder
.willReturn(
aResponse().withStatus(200)
.withBody(outputBody)));
}
@BeforeTest
public void resetWiremock() {
setupServer();
}

@Test
public void testHeaderBuilderMethod() {
stub("BuilderHeader", "BuilderHeaderValue");

RestClientBuilder builder = RestClientBuilder.newBuilder().baseUri(getServerURI());
builder.header("BuilderHeader", "BuilderHeaderValue");
SimpleGetApi client = builder.build(SimpleGetApi.class);

assertEquals(client.executeGet(),
"BuilderHeaderValue");
}

@Test
public void testHeaderBuilderMethodNullValue() {
stub("BuilderHeader", "BuilderHeaderValue");

RestClientBuilder builder = RestClientBuilder.newBuilder().baseUri(getServerURI());
try {
builder.header("BuilderHeader", null);
} catch (NullPointerException npe) {
return;
}
fail("header(\"builderHeader\", null) should have thrown a NullPointerException");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ private static ClientHeaderParamClient client(Class<?>... providers) {
for (Class<?> provider : providers) {
builder.register(provider);
}
builder.header("BuilderHeader", "BuilderHeaderValue");
return builder.build(ClientHeaderParamClient.class);
} catch (Throwable t) {
t.printStackTrace();
Expand Down Expand Up @@ -177,6 +178,7 @@ public void testHeaderNotSentWhenExceptionThrownAndRequiredIsFalse() {
assertEquals(headers.getString("OverrideableExplicit"), "overrideableInterfaceExplicit");
assertEquals(headers.getString("InterfaceHeaderComputed"), "interfaceComputed");
assertEquals(headers.getString("MethodHeaderExplicit"), "SomeValue");
assertEquals(headers.getString("BuilderHeaderValue"), "BuilderHeaderValue");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ private static ClientHeadersFactoryClient client(Class<?>... providers) {
for (Class<?> provider : providers) {
builder.register(provider);
}
builder.header("myHeader", "myHeaderValue");
return builder.build(ClientHeadersFactoryClient.class);
} catch (Throwable t) {
t.printStackTrace();
Expand All @@ -79,5 +80,6 @@ public void testClientHeadersFactoryInvoked() {
assertEquals(headers.getString("MethodHeader"), "methodValueModified");
assertEquals(headers.getString("ArgHeader"), "argValueModified");
assertEquals(headers.getString("FactoryHeader"), "factoryValue");
assertEquals(headers.getString("BuilderHeader"), "BuilderHeaderValue");
}
}

0 comments on commit b16fe10

Please sign in to comment.