• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

box / box-java-sdk-gen / #258

23 Jun 2025 02:39PM UTC coverage: 34.836% (-0.9%) from 35.719%
#258

push

github

web-flow
feat: add hubs API beta endpoints (box/box-openapi#531) (#341)

36 of 1240 new or added lines in 43 files covered. (2.9%)

9 existing lines in 4 files now uncovered.

16201 of 46506 relevant lines covered (34.84%)

0.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/java/com/box/sdkgen/networking/defaultnetworkclient/DefaultNetworkClient.java
1
package com.box.sdkgen.networking.defaultnetworkclient;
2

3
import static com.box.sdkgen.serialization.json.JsonManager.jsonToSerializedData;
4
import static com.box.sdkgen.serialization.json.JsonManager.sdToJson;
5
import static com.box.sdkgen.serialization.json.JsonManager.sdToUrlParams;
6
import static java.util.Collections.singletonList;
7
import static okhttp3.ConnectionSpec.MODERN_TLS;
8

9
import com.box.sdkgen.box.errors.BoxSDKError;
10
import com.box.sdkgen.networking.fetchoptions.FetchOptions;
11
import com.box.sdkgen.networking.fetchoptions.MultipartItem;
12
import com.box.sdkgen.networking.fetchoptions.ResponseFormat;
13
import com.box.sdkgen.networking.fetchresponse.FetchResponse;
14
import com.box.sdkgen.networking.network.NetworkSession;
15
import com.box.sdkgen.networking.networkclient.NetworkClient;
16
import java.io.IOException;
17
import java.util.Map;
18
import java.util.Objects;
19
import java.util.TreeMap;
20
import java.util.concurrent.TimeUnit;
21
import java.util.stream.Collectors;
22
import okhttp3.Call;
23
import okhttp3.Headers;
24
import okhttp3.HttpUrl;
25
import okhttp3.MediaType;
26
import okhttp3.MultipartBody;
27
import okhttp3.OkHttpClient;
28
import okhttp3.Request;
29
import okhttp3.RequestBody;
30
import okhttp3.Response;
31
import okio.BufferedSink;
32
import okio.Okio;
33
import okio.Source;
34

35
public class DefaultNetworkClient implements NetworkClient {
36

37
  protected OkHttpClient httpClient;
38

39
  public DefaultNetworkClient(OkHttpClient httpClient) {
×
40
    this.httpClient = httpClient;
×
41
  }
×
42

43
  public DefaultNetworkClient() {
×
44
    OkHttpClient.Builder builder =
×
45
        new OkHttpClient.Builder()
46
            .followSslRedirects(true)
×
47
            .followRedirects(false)
×
48
            .connectionSpecs(singletonList(MODERN_TLS));
×
49
    httpClient = builder.build();
×
50
  }
×
51

52
  public FetchResponse fetch(FetchOptions options) {
UNCOV
53
    NetworkSession networkSession =
×
54
        options.getNetworkSession() == null ? new NetworkSession() : options.getNetworkSession();
×
55

56
    FetchOptions fetchOptions =
×
57
        networkSession.getInterceptors().stream()
×
58
            .reduce(
×
59
                options,
60
                (modifiedOptions, interceptor) -> interceptor.beforeRequest(modifiedOptions),
×
61
                (o1, o2) -> o2);
×
62

63
    boolean authenticationNeeded = false;
×
64
    Request request;
65
    FetchResponse fetchResponse = null;
×
66
    Exception exceptionThrown = null;
×
67

68
    int attemptNumber = 0;
×
69

70
    while (true) {
71
      request = prepareRequest(fetchOptions, authenticationNeeded, networkSession);
×
72

73
      Response response = null;
×
74
      try {
75
        response = executeOnClient(request);
×
76

77
        Map<String, String> headersMap =
×
78
            response.headers().toMultimap().entrySet().stream()
×
79
                .collect(
×
80
                    Collectors.toMap(
×
81
                        Map.Entry::getKey,
82
                        e -> e.getValue().get(0),
×
83
                        (existing, replacement) -> existing,
×
84
                        () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER)));
×
85

UNCOV
86
        String responseUrl =
×
87
            response.networkResponse() != null
×
88
                ? response.networkResponse().request().url().toString()
×
89
                : response.request().url().toString();
×
UNCOV
90
        fetchResponse =
×
91
            Objects.equals(fetchOptions.getResponseFormat().getEnumValue(), ResponseFormat.BINARY)
×
92
                ? new FetchResponse.Builder(response.code(), headersMap)
×
93
                    .content(response.body().byteStream())
×
94
                    .url(responseUrl)
×
95
                    .build()
×
96
                : new FetchResponse.Builder(response.code(), headersMap)
×
97
                    .data(
×
98
                        response.body() != null
×
99
                            ? jsonToSerializedData(response.body().string())
×
100
                            : null)
101
                    .url(responseUrl)
×
102
                    .build();
×
103

104
        fetchResponse =
×
105
            networkSession.getInterceptors().stream()
×
106
                .reduce(
×
107
                    fetchResponse,
108
                    (modifiedResponse, interceptor) -> interceptor.afterRequest(modifiedResponse),
×
109
                    (o1, o2) -> o2);
×
110

111
        boolean shouldRetry =
×
112
            networkSession
113
                .getRetryStrategy()
×
114
                .shouldRetry(fetchOptions, fetchResponse, attemptNumber);
×
115

116
        if (shouldRetry) {
×
117
          TimeUnit.SECONDS.sleep(
×
118
              (long)
119
                  networkSession
120
                      .getRetryStrategy()
×
121
                      .retryAfter(fetchOptions, fetchResponse, attemptNumber));
×
122
          attemptNumber++;
×
123
          continue;
×
124
        }
125

126
        if (fetchResponse.getStatus() >= 300
×
127
            && fetchResponse.getStatus() < 400
×
128
            && fetchOptions.followRedirects) {
×
129
          if (!fetchResponse.getHeaders().containsKey("Location")) {
×
130
            throw new BoxSDKError(
×
131
                "Redirect response missing Location header for " + fetchOptions.getUrl());
×
132
          }
133
          return fetch(
×
134
              new FetchOptions.Builder(fetchResponse.getHeaders().get("Location"), "GET")
×
135
                  .responseFormat(fetchOptions.getResponseFormat())
×
136
                  .auth(fetchOptions.getAuth())
×
137
                  .networkSession(networkSession)
×
138
                  .build());
×
139
        }
140

141
      } catch (Exception e) {
×
142
        exceptionThrown = e;
×
143
        // Retry network exception only once
144
        if (attemptNumber > 1) {
×
145
          if (response != null) {
×
146
            response.close();
×
147
          }
148
          throw new BoxSDKError(e.getMessage(), e);
×
149
        }
150
      }
×
151

152
      if (fetchResponse != null
×
153
          && fetchResponse.getStatus() >= 200
×
154
          && fetchResponse.getStatus() < 400) {
×
155
        return fetchResponse;
×
156
      }
157

158
      throwOnUnsuccessfulResponse(request, fetchResponse, exceptionThrown);
×
159
    }
×
160
  }
161

162
  private static Request prepareRequest(
163
      FetchOptions options, boolean reauthenticate, NetworkSession networkSession) {
164
    Request.Builder requestBuilder = new Request.Builder().url(options.getUrl());
×
165
    Headers headers = prepareHeaders(options, reauthenticate, networkSession);
×
166
    HttpUrl url = prepareUrl(options);
×
167
    RequestBody body = prepareRequestBody(options);
×
168

169
    requestBuilder.headers(headers);
×
170
    requestBuilder.url(url);
×
171
    requestBuilder.method(options.getMethod().toUpperCase(), body);
×
172
    return requestBuilder.build();
×
173
  }
174

175
  private static Headers prepareHeaders(
176
      FetchOptions options, boolean reauthenticate, NetworkSession networkSession) {
177
    Headers.Builder headersBuilder = new Headers.Builder();
×
178

179
    networkSession.getAdditionalHeaders().forEach(headersBuilder::add);
×
180

181
    if (options.getHeaders() != null) {
×
182
      options.getHeaders().forEach(headersBuilder::add);
×
183
    }
184
    if (options.getAuth() != null) {
×
185
      if (reauthenticate) {
×
186
        options.getAuth().refreshToken(networkSession);
×
187
      }
188
      headersBuilder.add(
×
189
          "Authorization", options.getAuth().retrieveAuthorizationHeader(networkSession));
×
190
    }
191
    return headersBuilder.build();
×
192
  }
193

194
  private static HttpUrl prepareUrl(FetchOptions options) {
195

196
    HttpUrl baseUrl = HttpUrl.parse(options.getUrl());
×
197
    if (baseUrl == null) {
×
198
      throw new IllegalArgumentException("Invalid URL " + options.getUrl());
×
199
    }
200
    HttpUrl.Builder urlBuilder = baseUrl.newBuilder();
×
201
    if (options.getParams() != null) {
×
202
      options.getParams().forEach(urlBuilder::addQueryParameter);
×
203
    }
204
    return urlBuilder.build();
×
205
  }
206

207
  private static RequestBody prepareRequestBody(FetchOptions options) {
208
    if (options.getMethod().equalsIgnoreCase("GET")) {
×
209
      return null;
×
210
    }
211
    String contentType = options.getContentType();
×
212
    MediaType mediaType = MediaType.parse(contentType);
×
213
    switch (contentType) {
×
214
      case "application/json":
215
      case "application/json-patch+json":
216
        return options.getData() != null
×
217
            ? RequestBody.create(sdToJson(options.getData()), mediaType)
×
218
            : RequestBody.create("", mediaType);
×
219
      case "application/x-www-form-urlencoded":
220
        return options.getData() != null
×
221
            ? RequestBody.create(sdToUrlParams(options.getData()), mediaType)
×
222
            : RequestBody.create("", mediaType);
×
223
      case "multipart/form-data":
224
        MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
×
225
        for (MultipartItem part : options.multipartData) {
×
226
          if (part.getData() != null) {
×
227
            bodyBuilder.addFormDataPart(part.getPartName(), sdToJson(part.getData()));
×
228
          } else {
229
            bodyBuilder.addFormDataPart(
×
230
                part.getPartName(),
×
231
                part.getFileName() != null ? part.getFileName() : "file",
×
232
                createMultipartRequestBody(part));
×
233
          }
234
        }
×
235
        return bodyBuilder.build();
×
236
      default:
237
        throw new IllegalArgumentException("Unsupported content type " + contentType);
×
238
    }
239
  }
240

241
  protected Call createNewCall(Request request) {
242
    return this.httpClient.newCall(request);
×
243
  }
244

245
  private Response executeOnClient(Request request) {
246
    try {
247
      return createNewCall(request).execute();
×
248
    } catch (IOException e) {
×
249
      throw new RuntimeException(e);
×
250
    }
251
  }
252

253
  public static RequestBody createMultipartRequestBody(MultipartItem part) {
254
    return new RequestBody() {
×
255
      @Override
256
      public MediaType contentType() {
257
        if (part.contentType != null) {
×
258
          return MediaType.parse(part.contentType);
×
259
        }
260
        return MediaType.parse("application/octet-stream");
×
261
      }
262

263
      @Override
264
      public void writeTo(BufferedSink sink) throws IOException {
265
        try (Source source = Okio.source(part.getFileStream())) {
×
266
          sink.writeAll(source);
×
267
        }
268
      }
×
269
    };
270
  }
271

272
  private static void throwOnUnsuccessfulResponse(
273
      Request request, FetchResponse fetchResponse, Exception exceptionThrown) {
274
    if (fetchResponse == null) {
×
275
      throw new RuntimeException(exceptionThrown.getMessage(), exceptionThrown);
×
276
    }
277
    throw new RuntimeException(fetchResponse.getData().toString());
×
278
  }
279
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc