Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,19 @@ public abstract class DoclingServeClient extends HttpOperations implements Docli
private final TaskOperations taskOps;

protected DoclingServeClient(DoclingServeClientBuilder builder) {
this.baseUrl = ensureNotNull(builder.baseUrl, "baseUrl");
var base = ensureNotNull(builder.baseUrl, "baseUrl");

if (Objects.equals(this.baseUrl.getScheme(), "http")) {
if (Objects.equals(base.getScheme(), "http")) {
// Docling Serve uses Python FastAPI which causes errors when called from JDK HttpClient.
// The HttpClient uses HTTP 2 by default and then falls back to HTTP 1.1 if not supported.
// However, the way FastAPI works results in the fallback not happening, making the call fail.
builder.httpClientBuilder.version(HttpClient.Version.HTTP_1_1);
}

this.baseUrl = !base.getPath().endsWith("/") ?
URI.create(base + "/") :
base;

this.httpClient = ensureNotNull(builder.httpClientBuilder, "httpClientBuilder").build();
this.logRequests = builder.logRequests;
this.logResponses = builder.logResponses;
Expand Down Expand Up @@ -229,7 +233,7 @@ protected <I, O> O executeGet(RequestContext<I, O> requestContext) {

protected <I, O> HttpRequest.Builder createRequestBuilder(RequestContext<I, O> requestContext) {
var requestBuilder = HttpRequest.newBuilder()
.uri(this.baseUrl.resolve(requestContext.getUri()))
.uri(this.baseUrl.resolve(resolvePath(requestContext.getUri())))
.header("Accept", "application/json");

if (Utils.isNotNullOrBlank(this.apiKey)) {
Expand All @@ -239,6 +243,13 @@ protected <I, O> HttpRequest.Builder createRequestBuilder(RequestContext<I, O> r
return requestBuilder;
}

private String resolvePath(String path) {
return Optional.ofNullable(path)
.filter(p -> p.startsWith("/") && (p.length() > 1))
.map(p -> p.substring(1))
.orElse(path);
}

protected <T> T getResponse(HttpRequest request, HttpResponse<String> response, Class<T> expectedReturnType) {
var body = response.body();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath;
import static com.github.tomakehurst.wiremock.client.WireMock.okJson;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
Expand Down Expand Up @@ -43,6 +47,7 @@
import org.slf4j.LoggerFactory;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;

import ai.docling.core.DoclingDocument;
import ai.docling.core.DoclingDocument.DocItemLabel;
Expand Down Expand Up @@ -107,13 +112,13 @@ public void testFailed(ExtensionContext context, @Nullable Throwable cause) {
}
};

// @RegisterExtension
// static WireMockExtension wireMockExtension = WireMockExtension.newInstance()
// .options(wireMockConfig().dynamicPort())
// .configureStaticDsl(true)
// .failOnUnmatchedRequests(true)
// .resetOnEachTest(true)
// .build();
@RegisterExtension
static WireMockExtension wireMockExtension = WireMockExtension.newInstance()
.options(wireMockConfig().dynamicPort())
.configureStaticDsl(true)
.failOnUnmatchedRequests(true)
.resetOnEachTest(true)
.build();

static {
doclingContainer.start();
Expand Down Expand Up @@ -155,6 +160,37 @@ void builderWorks() {
.isInstanceOf(DoclingServeClient.class);
}

@Test
void pathPartFromBaseUrl() {
// From https://github.com/docling-project/docling-java/issues/294
var wiremockRuntimeInfo = wireMockExtension.getRuntimeInfo();
wireMockExtension.stubFor(
get(urlPathEqualTo("/path/health"))
.withHeader("Accept", equalTo("application/json"))
.willReturn(okJson("{\"status\": \"ok\"}"))
);

var baseUrl = "http://localhost:%d/path".formatted(wiremockRuntimeInfo.getHttpPort());

var client = DoclingServeApi.builder()
.logRequests()
.logResponses()
.prettyPrint()
.baseUrl(baseUrl)
.build();

assertThat(client.health())
.isNotNull()
.extracting(HealthCheckResponse::getStatus)
.isEqualTo("ok");

verify(
1,
getRequestedFor(urlPathEqualTo("/path/health"))
.withHeader("Accept", equalTo("application/json"))
);
}

@Nested
class ClearTests {
@Test
Expand Down