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

DataBiosphere / consent / #6344

18 Aug 2025 06:24PM UTC coverage: 83.311% (-0.04%) from 83.355%
#6344

push

web-flow
DT-1636: Sync User Linked Account Status with ECM (#2640)

47 of 65 new or added lines in 8 files covered. (72.31%)

2 existing lines in 1 file now uncovered.

10972 of 13170 relevant lines covered (83.31%)

0.83 hits per line

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

84.72
/src/main/java/org/broadinstitute/consent/http/util/HttpClientUtil.java
1
package org.broadinstitute.consent.http.util;
2

3
import com.codahale.metrics.MetricRegistry;
4
import com.codahale.metrics.SharedMetricRegistries;
5
import com.codahale.metrics.Timer;
6
import com.google.api.client.http.GenericUrl;
7
import com.google.api.client.http.HttpContent;
8
import com.google.api.client.http.HttpHeaders;
9
import com.google.api.client.http.HttpRequest;
10
import com.google.api.client.http.HttpResponse;
11
import com.google.api.client.http.HttpStatusCodes;
12
import com.google.api.client.http.HttpTransport;
13
import com.google.api.client.http.javanet.NetHttpTransport;
14
import com.google.common.cache.CacheBuilder;
15
import com.google.common.cache.CacheLoader;
16
import com.google.common.cache.LoadingCache;
17
import jakarta.ws.rs.BadRequestException;
18
import jakarta.ws.rs.ForbiddenException;
19
import jakarta.ws.rs.NotAuthorizedException;
20
import jakarta.ws.rs.NotFoundException;
21
import jakarta.ws.rs.ServerErrorException;
22
import jakarta.ws.rs.core.MediaType;
23
import java.io.IOException;
24
import java.net.URI;
25
import java.nio.charset.Charset;
26
import java.util.concurrent.Executors;
27
import java.util.concurrent.ScheduledExecutorService;
28
import java.util.concurrent.TimeUnit;
29
import org.apache.commons.io.IOUtils;
30
import org.apache.hc.client5.http.classic.HttpClient;
31
import org.apache.hc.client5.http.classic.methods.HttpGet;
32
import org.apache.hc.client5.http.impl.classic.HttpClients;
33
import org.broadinstitute.consent.http.configurations.ServicesConfiguration;
34
import org.broadinstitute.consent.http.exceptions.ConsentConflictException;
35
import org.broadinstitute.consent.http.models.AuthUser;
36

37
public class HttpClientUtil implements ConsentLogger {
38

39
  public record SimpleResponse(int code, String entity) {
1✔
40

41
  }
42

43
  private final ServicesConfiguration configuration;
44

45
  private final LoadingCache<URI, SimpleResponse> cache;
46

47
  private final HttpClient httpClient;
48

49
  public HttpClientUtil(ServicesConfiguration configuration) {
1✔
50
    this.configuration = configuration;
1✔
51
    httpClient = HttpClients.createDefault();
1✔
52
    CacheLoader<URI, SimpleResponse> loader = new CacheLoader<>() {
1✔
53
      @Override
54
      public SimpleResponse load(URI uri) throws Exception {
55
        return getHttpResponse(new HttpGet(uri));
1✔
56
      }
57
    };
58
    this.cache = CacheBuilder
1✔
59
        .newBuilder()
1✔
60
        .expireAfterWrite(configuration.getCacheExpireMinutes(), TimeUnit.MINUTES)
1✔
61
        .build(loader);
1✔
62
  }
1✔
63

64
  /**
65
   * This returns the result of previously executed GET requests from a cache or executes them if
66
   * they are not yet cached.
67
   *
68
   * @param request The HttpGet request
69
   * @return SimpleResponse
70
   * @throws IOException The exception
71
   */
72
  public SimpleResponse getCachedResponse(HttpGet request) throws IOException {
73
    try {
74
      return cache.get(request.getUri());
1✔
75
    } catch (Exception e) {
×
76
      // Something went wrong with the cached version, log for followup
77
      logWarn(e.getMessage());
×
78
      return getHttpResponse(request);
×
79
    }
80
  }
81

82
  /**
83
   * This handles GET requests that could be subject to connection timeout errors by limiting the
84
   * request to a configured default number of seconds.
85
   *
86
   * @param request The HttpGet request
87
   * @return SimpleResponse
88
   * @throws IOException The exception
89
   */
90
  public SimpleResponse getHttpResponse(HttpGet request) throws IOException {
91
    final ScheduledExecutorService executor = Executors.newScheduledThreadPool(
1✔
92
        configuration.getPoolSize());
1✔
93
    executor.schedule(request::cancel, configuration.getTimeoutSeconds(), TimeUnit.SECONDS);
1✔
94
    return httpClient.execute(request, httpResponse ->
1✔
95
        new SimpleResponse(
1✔
96
            httpResponse.getCode(),
1✔
97
            IOUtils.toString(httpResponse.getEntity().getContent(), Charset.defaultCharset()))
1✔
98
    );
99
  }
100

101
  public HttpRequest buildGetRequest(GenericUrl genericUrl, AuthUser authUser) throws Exception {
102
    HttpTransport transport = new NetHttpTransport();
1✔
103
    HttpRequest request = transport.createRequestFactory().buildGetRequest(genericUrl);
1✔
104
    request.setHeaders(buildHeaders(authUser));
1✔
105
    return request;
1✔
106
  }
107

108
  public HttpRequest buildDeleteRequest(GenericUrl genericUrl, AuthUser authUser) throws Exception {
NEW
109
    HttpTransport transport = new NetHttpTransport();
×
NEW
110
    HttpRequest request = transport.createRequestFactory().buildDeleteRequest(genericUrl);
×
NEW
111
    request.setHeaders(buildHeaders(authUser));
×
NEW
112
    return request;
×
113
  }
114

115
  public HttpRequest buildUnAuthedGetRequest(GenericUrl genericUrl) throws Exception {
116
    HttpTransport transport = new NetHttpTransport();
1✔
117
    HttpRequest request = transport.createRequestFactory().buildGetRequest(genericUrl);
1✔
118
    request.setHeaders(new HttpHeaders().set("X-App-ID", "DUOS"));
1✔
119
    return request;
1✔
120
  }
121

122
  public HttpRequest buildPostRequest(GenericUrl genericUrl, HttpContent content, AuthUser authUser)
123
      throws Exception {
124
    HttpTransport transport = new NetHttpTransport();
1✔
125
    HttpRequest request = transport.createRequestFactory().buildPostRequest(genericUrl, content);
1✔
126
    request.setHeaders(buildHeaders(authUser));
1✔
127
    return request;
1✔
128
  }
129

130
  public HttpRequest buildPutRequest(GenericUrl genericUrl, HttpContent content, AuthUser authUser)
131
      throws Exception {
132
    HttpTransport transport = new NetHttpTransport();
1✔
133
    HttpRequest request = transport.createRequestFactory().buildPutRequest(genericUrl, content);
1✔
134
    request.setHeaders(buildHeaders(authUser));
1✔
135
    return request;
1✔
136
  }
137

138
  public HttpRequest buildUnAuthedPostRequest(GenericUrl genericUrl, HttpContent content)
139
      throws Exception {
140
    HttpTransport transport = new NetHttpTransport();
1✔
141
    HttpRequest request = transport.createRequestFactory().buildPostRequest(genericUrl, content);
1✔
142
    request.setHeaders(new HttpHeaders().set("X-App-ID", "DUOS"));
1✔
143
    return request;
1✔
144
  }
145

146
  public HttpResponse handleHttpRequest(HttpRequest request) {
147
    String timerName = String.format("org.broadinstitute.consent.http.util.HttpClientUtil-%s-%s",
1✔
148
        request.getRequestMethod(), request.getUrl().toString());
1✔
149
    if (SharedMetricRegistries.tryGetDefault() == null) {
1✔
150
      logWarn("Default SharedMetricRegistries is null, setting default");
×
151
      SharedMetricRegistries.setDefault("org.broadinstitute.consent", new MetricRegistry());
×
152
    }
153
    Timer timer = SharedMetricRegistries.getDefault().timer(timerName);
1✔
154
    try {
155
      request.setThrowExceptionOnExecuteError(false);
1✔
156
      HttpResponse response;
157
      try (Timer.Context context = timer.time()) {
1✔
158
        response = request.execute();
1✔
159
        context.stop();
1✔
160
      }
161
      if (response != null) {
1✔
162
        return switch (response.getStatusCode()) {
1✔
163
          case HttpStatusCodes.STATUS_CODE_BAD_REQUEST ->
164
              throw new BadRequestException(response.getStatusMessage());
1✔
165
          case HttpStatusCodes.STATUS_CODE_UNAUTHORIZED ->
166
              throw new NotAuthorizedException(response.getStatusMessage());
1✔
167
          case HttpStatusCodes.STATUS_CODE_FORBIDDEN ->
168
              throw new ForbiddenException(response.getStatusMessage());
1✔
169
          case HttpStatusCodes.STATUS_CODE_NOT_FOUND ->
170
              throw new NotFoundException(response.getStatusMessage());
1✔
171
          case HttpStatusCodes.STATUS_CODE_CONFLICT ->
172
              throw new ConsentConflictException(response.getStatusMessage());
1✔
173
          default -> response;
1✔
174
        };
175
      }
176
    } catch (IOException e) {
1✔
177
      throw new ServerErrorException("Server Error (" + e.getMessage() + ")", HttpStatusCodes.STATUS_CODE_SERVER_ERROR);
1✔
178
    }
×
179
    throw new ServerErrorException("Server Error", HttpStatusCodes.STATUS_CODE_SERVER_ERROR);
×
180
  }
181

182
  private HttpHeaders buildHeaders(AuthUser authUser) {
183
    return new HttpHeaders()
1✔
184
        .setAuthorization("Bearer " + authUser.getAuthToken())
1✔
185
        .setAccept(MediaType.APPLICATION_JSON)
1✔
186
        .set("X-App-ID", "DUOS");
1✔
187
  }
188
}
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

© 2025 Coveralls, Inc