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

box / box-java-sdk-gen / #304

26 Jun 2025 03:33PM UTC coverage: 35.661% (-0.02%) from 35.681%
#304

push

github

web-flow
feat: Add webhook validation(box/box-codegen#745) (#347)

Co-authored-by: box-sdk-build <box-sdk-build@box.com>

68 of 82 new or added lines in 2 files covered. (82.93%)

66 existing lines in 7 files now uncovered.

16937 of 47495 relevant lines covered (35.66%)

0.36 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.Locale;
18
import java.util.Map;
19
import java.util.Objects;
20
import java.util.TreeMap;
21
import java.util.concurrent.TimeUnit;
22
import java.util.stream.Collectors;
23
import okhttp3.Call;
24
import okhttp3.Headers;
25
import okhttp3.HttpUrl;
26
import okhttp3.MediaType;
27
import okhttp3.MultipartBody;
28
import okhttp3.OkHttpClient;
29
import okhttp3.Request;
30
import okhttp3.RequestBody;
31
import okhttp3.Response;
32
import okio.BufferedSink;
33
import okio.Okio;
34
import okio.Source;
35

36
public class DefaultNetworkClient implements NetworkClient {
37

38
  protected OkHttpClient httpClient;
39

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

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

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

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

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

69
    int attemptNumber = 0;
×
70

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

195
  private static HttpUrl prepareUrl(FetchOptions options) {
196

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

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

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

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

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

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

273
  private static void throwOnUnsuccessfulResponse(
274
      Request request, FetchResponse fetchResponse, Exception exceptionThrown) {
275
    if (fetchResponse == null) {
×
276
      throw new RuntimeException(exceptionThrown.getMessage(), exceptionThrown);
×
277
    }
278
    throw new RuntimeException(fetchResponse.getData().toString());
×
279
  }
280
}
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