Skip to content

Commit 596d698

Browse files
committed
Merge branch 'main' into html-page-redesign
2 parents b30c19b + e173db5 commit 596d698

File tree

22 files changed

+326
-333
lines changed

22 files changed

+326
-333
lines changed

core/pom.xml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@
3030
</developers>
3131
<properties>
3232
<project.rootdir>${project.basedir}/../</project.rootdir>
33+
<coverage.complexity>60%</coverage.complexity>
34+
<coverage.line>69%</coverage.line>
35+
<coverage.instruction>68%</coverage.instruction>
36+
<coverage.branch>52%</coverage.branch>
37+
<coverage.method>73%</coverage.method>
38+
<coverage.class>87%</coverage.class>
3339
</properties>
3440

3541
<dependencies>
@@ -66,6 +72,10 @@
6672
<groupId>org.apache.httpcomponents.client5</groupId>
6773
<artifactId>httpclient5</artifactId>
6874
</dependency>
75+
<dependency>
76+
<groupId>org.apache.httpcomponents.core5</groupId>
77+
<artifactId>httpcore5</artifactId>
78+
</dependency>
6979
<dependency>
7080
<groupId>com.google.code.findbugs</groupId>
7181
<artifactId>jsr305</artifactId>
@@ -129,11 +139,6 @@
129139
<artifactId>wiremock</artifactId>
130140
<scope>test</scope>
131141
</dependency>
132-
<dependency>
133-
<groupId>org.apache.httpcomponents.core5</groupId>
134-
<artifactId>httpcore5</artifactId>
135-
<scope>test</scope>
136-
</dependency>
137142
<dependency>
138143
<groupId>org.assertj</groupId>
139144
<artifactId>assertj-core</artifactId>
@@ -161,8 +166,6 @@
161166
<groupId>com.sap.cloud.sdk.datamodel</groupId>
162167
<artifactId>openapi-generator-maven-plugin</artifactId>
163168
<configuration>
164-
<skip>true</skip>
165-
<!-- TODO: remove this once Cloud SDK 5.15.0 is released -->
166169
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
167170
<apiMaturity>released</apiMaturity>
168171
<enableOneOfAnyOfGeneration>true</enableOneOfAnyOfGeneration>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.sap.ai.sdk.core.commons;
2+
3+
import com.google.common.annotations.Beta;
4+
import javax.annotation.Nullable;
5+
6+
/**
7+
* Generic class that contains a JSON error response.
8+
*
9+
* @since 1.1.0
10+
*/
11+
@Beta
12+
@FunctionalInterface
13+
public interface ClientError {
14+
/**
15+
* Get the error message.
16+
*
17+
* @return The error message
18+
*/
19+
@Nullable
20+
String getMessage();
21+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.sap.ai.sdk.core.commons;
2+
3+
import com.google.common.annotations.Beta;
4+
import lombok.experimental.StandardException;
5+
6+
/**
7+
* Generic exception for errors occurring when using AI SDK clients.
8+
*
9+
* @since 1.1.0
10+
*/
11+
@Beta
12+
@StandardException
13+
public class ClientException extends RuntimeException {}

orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationResponseHandler.java renamed to core/src/main/java/com/sap/ai/sdk/core/commons/ClientResponseHandler.java

Lines changed: 75 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
package com.sap.ai.sdk.orchestration;
1+
package com.sap.ai.sdk.core.commons;
22

3-
import static com.sap.ai.sdk.orchestration.OrchestrationClient.JACKSON;
3+
import static com.sap.ai.sdk.core.JacksonConfiguration.getDefaultObjectMapper;
44

55
import com.fasterxml.jackson.core.JsonProcessingException;
6-
import com.sap.ai.sdk.orchestration.model.ErrorResponse;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.google.common.annotations.Beta;
78
import io.vavr.control.Try;
89
import java.io.IOException;
910
import java.nio.charset.StandardCharsets;
11+
import java.util.Objects;
12+
import java.util.function.BiFunction;
1013
import javax.annotation.Nonnull;
1114
import lombok.RequiredArgsConstructor;
1215
import lombok.extern.slf4j.Slf4j;
@@ -18,28 +21,80 @@
1821
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
1922
import org.apache.hc.core5.http.io.entity.EntityUtils;
2023

24+
/**
25+
* Parse incoming JSON responses and handles any errors. For internal use only.
26+
*
27+
* @param <T> The type of the response.
28+
* @param <E> The type of the exception to throw.
29+
* @since 1.1.0
30+
*/
31+
@Beta
2132
@Slf4j
2233
@RequiredArgsConstructor
23-
class OrchestrationResponseHandler<T> implements HttpClientResponseHandler<T> {
34+
public class ClientResponseHandler<T, E extends ClientException>
35+
implements HttpClientResponseHandler<T> {
2436
@Nonnull private final Class<T> responseType;
37+
@Nonnull private final Class<? extends ClientError> errorType;
38+
@Nonnull private final BiFunction<String, Throwable, E> exceptionType;
2539

40+
/** The parses for JSON responses, will be private once we can remove mixins */
41+
@Nonnull public ObjectMapper JACKSON = getDefaultObjectMapper();
42+
43+
/**
44+
* Processes a {@link ClassicHttpResponse} and returns some value corresponding to that response.
45+
*
46+
* @param response The response to process
47+
* @return A model class instantiated from the response
48+
* @throws E in case of a problem or the connection was aborted
49+
*/
50+
@Nonnull
51+
@Override
52+
public T handleResponse(@Nonnull final ClassicHttpResponse response) throws E {
53+
if (response.getCode() >= 300) {
54+
buildExceptionAndThrow(response);
55+
}
56+
return parseResponse(response);
57+
}
58+
59+
// The InputStream of the HTTP entity is closed by EntityUtils.toString
60+
@SuppressWarnings("PMD.CloseResource")
2661
@Nonnull
27-
private static String getContent(@Nonnull final HttpEntity entity) {
62+
private T parseResponse(@Nonnull final ClassicHttpResponse response) throws E {
63+
final HttpEntity responseEntity = response.getEntity();
64+
if (responseEntity == null) {
65+
throw exceptionType.apply("Response was empty.", null);
66+
}
67+
val content = getContent(responseEntity);
68+
log.debug("Parsing response from JSON response: {}", content);
69+
try {
70+
return JACKSON.readValue(content, responseType);
71+
} catch (final JsonProcessingException e) {
72+
log.error("Failed to parse the following response: {}", content);
73+
throw exceptionType.apply("Failed to parse response", e);
74+
}
75+
}
76+
77+
@Nonnull
78+
private String getContent(@Nonnull final HttpEntity entity) {
2879
try {
2980
return EntityUtils.toString(entity, StandardCharsets.UTF_8);
3081
} catch (IOException | ParseException e) {
31-
throw new OrchestrationClientException("Failed to read response content.", e);
82+
throw exceptionType.apply("Failed to read response content.", e);
3283
}
3384
}
3485

35-
// The InputStream of the HTTP entity is closed by EntityUtils.toString
86+
/**
87+
* Parse the error response and throw an exception.
88+
*
89+
* @param response The response to process
90+
*/
3691
@SuppressWarnings("PMD.CloseResource")
37-
static void buildExceptionAndThrow(@Nonnull final ClassicHttpResponse response)
38-
throws OrchestrationClientException {
92+
public void buildExceptionAndThrow(@Nonnull final ClassicHttpResponse response) throws E {
3993
val exception =
40-
new OrchestrationClientException(
41-
"Request to orchestration service failed with status %s %s"
42-
.formatted(response.getCode(), response.getReasonPhrase()));
94+
exceptionType.apply(
95+
"Request failed with status %s %s"
96+
.formatted(response.getCode(), response.getReasonPhrase()),
97+
null);
4398
val entity = response.getEntity();
4499
if (entity == null) {
45100
throw exception;
@@ -54,9 +109,7 @@ static void buildExceptionAndThrow(@Nonnull final ClassicHttpResponse response)
54109
throw exception;
55110
}
56111

57-
log.error(
58-
"The orchestration service responded with an HTTP error and the following content: {}",
59-
content);
112+
log.error("The service responded with an HTTP error and the following content: {}", content);
60113
val contentType = ContentType.parse(entity.getContentType());
61114
if (!ContentType.APPLICATION_JSON.isSameMimeType(contentType)) {
62115
throw exception;
@@ -68,59 +121,19 @@ static void buildExceptionAndThrow(@Nonnull final ClassicHttpResponse response)
68121
/**
69122
* Parse the error response and throw an exception.
70123
*
71-
* @param errorResponse the error response, most likely a JSON of {@link ErrorResponse}.
124+
* @param errorResponse the error response, most likely a unique JSON class.
72125
* @param baseException a base exception to add the error message to.
73126
*/
74-
static void parseErrorAndThrow(
75-
@Nonnull final String errorResponse,
76-
@Nonnull final OrchestrationClientException baseException)
77-
throws OrchestrationClientException {
78-
val maybeError = Try.of(() -> JACKSON.readValue(errorResponse, ErrorResponse.class));
127+
public void parseErrorAndThrow(
128+
@Nonnull final String errorResponse, @Nonnull final E baseException) throws E {
129+
val maybeError = Try.of(() -> JACKSON.readValue(errorResponse, errorType));
79130
if (maybeError.isFailure()) {
80131
baseException.addSuppressed(maybeError.getCause());
81132
throw baseException;
82133
}
83134

84-
throw new OrchestrationClientException(
85-
"%s and error message: '%s'"
86-
.formatted(baseException.getMessage(), maybeError.get().getMessage()));
87-
}
88-
89-
/**
90-
* Processes a {@link ClassicHttpResponse} and returns some value corresponding to that response.
91-
*
92-
* @param response The response to process
93-
* @return A model class instantiated from the response
94-
* @throws OrchestrationClientException in case of a problem or the connection was aborted
95-
*/
96-
@Override
97-
public T handleResponse(@Nonnull final ClassicHttpResponse response)
98-
throws OrchestrationClientException {
99-
if (response.getCode() >= 300) {
100-
buildExceptionAndThrow(response);
101-
}
102-
val result = parseResponse(response);
103-
log.debug("Received the following response from orchestration service: {}", result);
104-
return result;
105-
}
106-
107-
// The InputStream of the HTTP entity is closed by EntityUtils.toString
108-
@SuppressWarnings("PMD.CloseResource")
109-
@Nonnull
110-
private T parseResponse(@Nonnull final ClassicHttpResponse response)
111-
throws OrchestrationClientException {
112-
final HttpEntity responseEntity = response.getEntity();
113-
if (responseEntity == null) {
114-
throw new OrchestrationClientException("Response from Orchestration service was empty.");
115-
}
116-
val content = getContent(responseEntity);
117-
log.debug("Parsing response from JSON response: {}", content);
118-
try {
119-
return JACKSON.readValue(content, responseType);
120-
} catch (final JsonProcessingException e) {
121-
log.error("Failed to parse the following response from orchestration service: {}", content);
122-
throw new OrchestrationClientException(
123-
"Failed to parse response from orchestration service", e);
124-
}
135+
val error = Objects.requireNonNullElse(maybeError.get().getMessage(), "");
136+
val message = "%s and error message: '%s'".formatted(baseException.getMessage(), error);
137+
throw exceptionType.apply(message, baseException);
125138
}
126139
}

core/src/main/java/com/sap/ai/sdk/core/model/FileDownload400Response.java renamed to core/src/main/java/com/sap/ai/sdk/core/model/InlineObject.java

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
import javax.annotation.Nonnull;
2525
import javax.annotation.Nullable;
2626

27-
/** FileDownload400Response */
27+
/** InlineObject */
2828
// CHECKSTYLE:OFF
29-
public class FileDownload400Response
29+
public class InlineObject
3030
// CHECKSTYLE:ON
3131
{
3232
@JsonProperty("error")
@@ -35,42 +35,42 @@ public class FileDownload400Response
3535
@JsonAnySetter @JsonAnyGetter
3636
private final Map<String, Object> cloudSdkCustomFields = new LinkedHashMap<>();
3737

38-
/** Default constructor for FileDownload400Response. */
39-
protected FileDownload400Response() {}
38+
/** Default constructor for InlineObject. */
39+
protected InlineObject() {}
4040

4141
/**
42-
* Set the error of this {@link FileDownload400Response} instance and return the same instance.
42+
* Set the error of this {@link InlineObject} instance and return the same instance.
4343
*
44-
* @param error The error of this {@link FileDownload400Response}
45-
* @return The same instance of this {@link FileDownload400Response} class
44+
* @param error The error of this {@link InlineObject}
45+
* @return The same instance of this {@link InlineObject} class
4646
*/
4747
@Nonnull
48-
public FileDownload400Response error(@Nullable final DSetError error) {
48+
public InlineObject error(@Nullable final DSetError error) {
4949
this.error = error;
5050
return this;
5151
}
5252

5353
/**
5454
* Get error
5555
*
56-
* @return error The error of this {@link FileDownload400Response} instance.
56+
* @return error The error of this {@link InlineObject} instance.
5757
*/
5858
@Nonnull
5959
public DSetError getError() {
6060
return error;
6161
}
6262

6363
/**
64-
* Set the error of this {@link FileDownload400Response} instance.
64+
* Set the error of this {@link InlineObject} instance.
6565
*
66-
* @param error The error of this {@link FileDownload400Response}
66+
* @param error The error of this {@link InlineObject}
6767
*/
6868
public void setError(@Nullable final DSetError error) {
6969
this.error = error;
7070
}
7171

7272
/**
73-
* Get the names of the unrecognizable properties of the {@link FileDownload400Response}.
73+
* Get the names of the unrecognizable properties of the {@link InlineObject}.
7474
*
7575
* @return The set of properties names
7676
*/
@@ -81,7 +81,7 @@ public Set<String> getCustomFieldNames() {
8181
}
8282

8383
/**
84-
* Get the value of an unrecognizable property of this {@link FileDownload400Response} instance.
84+
* Get the value of an unrecognizable property of this {@link InlineObject} instance.
8585
*
8686
* @param name The name of the property
8787
* @return The value of the property
@@ -90,15 +90,14 @@ public Set<String> getCustomFieldNames() {
9090
@Nullable
9191
public Object getCustomField(@Nonnull final String name) throws NoSuchElementException {
9292
if (!cloudSdkCustomFields.containsKey(name)) {
93-
throw new NoSuchElementException(
94-
"FileDownload400Response has no field with name '" + name + "'.");
93+
throw new NoSuchElementException("InlineObject has no field with name '" + name + "'.");
9594
}
9695
return cloudSdkCustomFields.get(name);
9796
}
9897

9998
/**
100-
* Set an unrecognizable property of this {@link FileDownload400Response} instance. If the map
101-
* previously contained a mapping for the key, the old value is replaced by the specified value.
99+
* Set an unrecognizable property of this {@link InlineObject} instance. If the map previously
100+
* contained a mapping for the key, the old value is replaced by the specified value.
102101
*
103102
* @param customFieldName The name of the property
104103
* @param customFieldValue The value of the property
@@ -116,9 +115,9 @@ public boolean equals(@Nullable final java.lang.Object o) {
116115
if (o == null || getClass() != o.getClass()) {
117116
return false;
118117
}
119-
final FileDownload400Response fileDownload400Response = (FileDownload400Response) o;
120-
return Objects.equals(this.cloudSdkCustomFields, fileDownload400Response.cloudSdkCustomFields)
121-
&& Objects.equals(this.error, fileDownload400Response.error);
118+
final InlineObject inlineObject = (InlineObject) o;
119+
return Objects.equals(this.cloudSdkCustomFields, inlineObject.cloudSdkCustomFields)
120+
&& Objects.equals(this.error, inlineObject.error);
122121
}
123122

124123
@Override
@@ -130,7 +129,7 @@ public int hashCode() {
130129
@Nonnull
131130
public String toString() {
132131
final StringBuilder sb = new StringBuilder();
133-
sb.append("class FileDownload400Response {\n");
132+
sb.append("class InlineObject {\n");
134133
sb.append(" error: ").append(toIndentedString(error)).append("\n");
135134
cloudSdkCustomFields.forEach(
136135
(k, v) ->
@@ -149,8 +148,8 @@ private String toIndentedString(final java.lang.Object o) {
149148
return o.toString().replace("\n", "\n ");
150149
}
151150

152-
/** Create a new {@link FileDownload400Response} instance. No arguments are required. */
153-
public static FileDownload400Response create() {
154-
return new FileDownload400Response();
151+
/** Create a new {@link InlineObject} instance. No arguments are required. */
152+
public static InlineObject create() {
153+
return new InlineObject();
155154
}
156155
}

0 commit comments

Comments
 (0)