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

CeON / dataverse / 1369

19 Jun 2024 01:24PM UTC coverage: 25.507% (-0.003%) from 25.51%
1369

push

jenkins

web-flow
Closes #2467: Add button to users dashboard to download csv with list of users (#2495)

* Closes #2478: API to download CSV file with all registered users

* Re-use existing superuser fetching method

* Closes #2467: Add button to users dashboard to download csv with list of users

4 of 14 new or added lines in 3 files covered. (28.57%)

1 existing line in 1 file now uncovered.

17782 of 69713 relevant lines covered (25.51%)

0.26 hits per line

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

21.72
/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java
1
package edu.harvard.iq.dataverse.api;
2

3
import com.fasterxml.jackson.databind.ObjectMapper;
4
import com.onelogin.saml2.Auth;
5
import edu.harvard.iq.dataverse.DataFileServiceBean;
6
import edu.harvard.iq.dataverse.DatasetDao;
7
import edu.harvard.iq.dataverse.DatasetFieldServiceBean;
8
import edu.harvard.iq.dataverse.DatasetLinkingServiceBean;
9
import edu.harvard.iq.dataverse.DataverseDao;
10
import edu.harvard.iq.dataverse.DataverseSession;
11
import edu.harvard.iq.dataverse.EjbDataverseEngine;
12
import edu.harvard.iq.dataverse.MetadataBlockDao;
13
import edu.harvard.iq.dataverse.UserServiceBean;
14
import edu.harvard.iq.dataverse.api.dto.ApiErrorResponseDTO;
15
import edu.harvard.iq.dataverse.api.dto.ApiResponseDTO;
16
import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean;
17
import edu.harvard.iq.dataverse.common.BundleUtil;
18
import edu.harvard.iq.dataverse.common.Util;
19
import edu.harvard.iq.dataverse.dataverse.DataverseLinkingService;
20
import edu.harvard.iq.dataverse.engine.command.Command;
21
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
22
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
23
import edu.harvard.iq.dataverse.engine.command.exception.IllegalCommandException;
24
import edu.harvard.iq.dataverse.engine.command.exception.PermissionException;
25
import edu.harvard.iq.dataverse.persistence.datafile.DataFile;
26
import edu.harvard.iq.dataverse.persistence.dataset.Dataset;
27
import edu.harvard.iq.dataverse.persistence.dataverse.Dataverse;
28
import edu.harvard.iq.dataverse.persistence.dataverse.link.DatasetLinkingDataverse;
29
import edu.harvard.iq.dataverse.persistence.dataverse.link.DataverseLinkingDataverse;
30
import edu.harvard.iq.dataverse.persistence.user.AuthenticatedUser;
31
import edu.harvard.iq.dataverse.persistence.user.GuestUser;
32
import edu.harvard.iq.dataverse.persistence.user.PrivateUrlUser;
33
import edu.harvard.iq.dataverse.persistence.user.User;
34
import edu.harvard.iq.dataverse.privateurl.PrivateUrlServiceBean;
35
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
36
import edu.harvard.iq.dataverse.util.SystemConfig;
37
import edu.harvard.iq.dataverse.util.json.JsonParser;
38
import io.vavr.control.Option;
39
import io.vavr.control.Try;
40
import org.apache.commons.lang.SerializationException;
41

42
import javax.ejb.EJB;
43
import javax.inject.Inject;
44
import javax.json.Json;
45
import javax.json.JsonArrayBuilder;
46
import javax.json.JsonObject;
47
import javax.json.JsonObjectBuilder;
48
import javax.json.JsonReader;
49
import javax.json.JsonValue;
50
import javax.json.JsonValue.ValueType;
51
import javax.persistence.EntityManager;
52
import javax.persistence.PersistenceContext;
53
import javax.servlet.http.HttpServletRequest;
54
import javax.ws.rs.core.Context;
55
import javax.ws.rs.core.MediaType;
56
import javax.ws.rs.core.Response;
57
import javax.ws.rs.core.Response.Status;
58
import java.io.StringReader;
59
import java.net.URI;
60
import java.util.UUID;
61
import java.util.concurrent.Callable;
62
import java.util.logging.Level;
63
import java.util.logging.Logger;
64

65
import static org.apache.commons.lang.StringUtils.isNumeric;
66

67
/**
68
 * Base class for API beans
69
 *
70
 * @author michael
71
 */
72
public abstract class AbstractApiBean {
1✔
73

74
    @EJB
75
    protected EjbDataverseEngine engineSvc;
76

77
    @EJB
78
    protected DatasetDao datasetSvc;
79

80
    @EJB
81
    protected DataFileServiceBean fileService;
82

83
    @EJB
84
    protected DataverseDao dataverseSvc;
85

86
    @EJB
87
    protected AuthenticationServiceBean authSvc;
88

89
    @EJB
90
    protected DatasetFieldServiceBean datasetFieldSvc;
91

92
    @EJB
93
    protected MetadataBlockDao metadataBlockSvc;
94

95
    @EJB
96
    protected UserServiceBean userSvc;
97

98
    @Inject
99
    protected SettingsServiceBean settingsSvc;
100

101
    @EJB
102
    protected PrivateUrlServiceBean privateUrlSvc;
103

104
    @EJB
105
    protected SystemConfig systemConfig;
106

107
    @EJB
108
    protected DatasetLinkingServiceBean dsLinkingService;
109

110
    @EJB
111
    protected DataverseLinkingService dvLinkingService;
112

113
    @Inject
114
    DataverseSession session;
115

116
    @PersistenceContext(unitName = "VDCNet-ejbPU")
117
    protected EntityManager em;
118

119
    @Context
120
    protected HttpServletRequest httpRequest;
121

122
    private static final Logger logger = Logger.getLogger(AbstractApiBean.class.getName());
1✔
123
    private static final String DATAVERSE_KEY_HEADER_NAME = "X-Dataverse-key";
124
    private static final String PERSISTENT_ID_KEY = ":persistentId";
125
    public static final String STATUS_ERROR = "ERROR";
126
    public static final String STATUS_OK = "OK";
127
    public static final String STATUS_WF_IN_PROGRESS = "WORKFLOW_IN_PROGRESS";
128

129
    /**
130
     * Utility class to convey a proper error response using Java's exceptions.
131
     */
132
    public static class WrappedResponse extends Exception {
133
        private final Response response;
134

135
        public WrappedResponse(Response response) {
1✔
136
            this.response = response;
1✔
137
        }
1✔
138

139
        public WrappedResponse(Throwable cause, Response response) {
140
            super(cause);
×
141
            this.response = response;
×
142
        }
×
143

144
        public Response getResponse() {
145
            return response;
×
146
        }
147

148
        /**
149
         * Creates a new response, based on the original response and the passed message.
150
         * Typical use would be to add a better error message to the HTTP response.
151
         *
152
         * @param message additional message to be added to the response.
153
         * @return A Response with updated message field.
154
         */
155
        public Response refineResponse(String message) {
156
            final Status statusCode = Response.Status.fromStatusCode(response.getStatus());
×
157
            String baseMessage = getWrappedMessageWhenJson();
×
158

159
            if (baseMessage == null) {
×
160
                final Throwable cause = getCause();
×
161
                baseMessage = (cause != null ? cause.getMessage() : "");
×
162
            }
163
            return error(statusCode, message + " " + baseMessage);
×
164
        }
165

166
        /**
167
         * In the common case of the wrapped response being of type JSON,
168
         * return the message field it has (if any).
169
         *
170
         * @return the content of a message field, or {@code null}.
171
         */
172
        String getWrappedMessageWhenJson() {
173
            if (response.getMediaType().equals(MediaType.APPLICATION_JSON_TYPE)) {
×
174
                Object entity = response.getEntity();
×
175
                if (entity == null) {
×
176
                    return null;
×
177
                }
178

179
                String json = entity.toString();
×
180
                try (StringReader rdr = new StringReader(json)) {
×
181
                    JsonReader jrdr = Json.createReader(rdr);
×
182
                    JsonObject obj = jrdr.readObject();
×
183
                    if (obj.containsKey("message")) {
×
184
                        JsonValue message = obj.get("message");
×
185
                        return message.getValueType() == ValueType.STRING ? obj.getString("message") : message.toString();
×
186
                    } else {
187
                        return null;
×
188
                    }
189
                }
190
            } else {
191
                return null;
×
192
            }
193
        }
194
    }
195

196

197
    private final LazyRef<JsonParser> jsonParserRef = new LazyRef<>(new Callable<JsonParser>() {
1✔
198
        @Override
199
        public JsonParser call() throws Exception {
200
            return new JsonParser(datasetFieldSvc, metadataBlockSvc, settingsSvc);
×
201
        }
202
    });
203

204
    /**
205
     * Functional interface for handling HTTP requests in the APIs.
206
     *
207
     * @see #response(edu.harvard.iq.dataverse.api.AbstractApiBean.DataverseRequestHandler)
208
     */
209
    protected interface DataverseRequestHandler {
210
        Response handle(DataverseRequest u) throws WrappedResponse;
211
    }
212

213

214
    /* ===================== *\
215
     *  Utility Methods      *
216
     *  Get that DSL feelin' *
217
    \* ===================== */
218

219
    protected JsonParser jsonParser() {
220
        return jsonParserRef.get();
×
221
    }
222

223
    protected boolean parseBooleanOrDie(String input) throws WrappedResponse {
224
        if (input == null) {
×
225
            throw new WrappedResponse(badRequest("Boolean value missing"));
×
226
        }
227
        input = input.trim();
×
228
        if (Util.isBoolean(input)) {
×
229
            return Util.isTrue(input);
×
230
        } else {
231
            throw new WrappedResponse(badRequest("Illegal boolean value '" + input + "'"));
×
232
        }
233
    }
234

235
    /**
236
     * Returns the {@code key} query parameter from the current request, or {@code null} if
237
     * the request has no such parameter.
238
     *
239
     * @param key Name of the requested parameter.
240
     * @return Value of the requested parameter in the current request.
241
     */
242
    protected String getRequestParameter(String key) {
243
        return httpRequest.getParameter(key);
×
244
    }
245

246
    protected String getRequestApiKey() {
247
        String headerParamApiKey = httpRequest.getHeader(DATAVERSE_KEY_HEADER_NAME);
1✔
248
        String queryParamApiKey = httpRequest.getParameter("key");
1✔
249

250
        return headerParamApiKey != null ? headerParamApiKey : queryParamApiKey;
1✔
251
    }
252

253
    /**
254
     * @param apiKey the key to find the user with
255
     * @return the user, or null
256
     * @see #findUserOrDie(java.lang.String)
257
     */
258
    protected AuthenticatedUser findUserByApiToken(String apiKey) {
259
        return authSvc.lookupUser(apiKey);
×
260
    }
261

262
    /**
263
     * Returns the user of pointed by the API key, or the guest user
264
     *
265
     * @return a user, may be a guest user.
266
     * @throws edu.harvard.iq.dataverse.api.AbstractApiBean.WrappedResponse iff there is an api key present, but it is invalid.
267
     */
268
    protected User findUserOrDie() throws WrappedResponse {
269
        final String requestApiKey = getRequestApiKey();
1✔
270
        if (requestApiKey == null) {
1✔
271
            return GuestUser.get();
1✔
272
        }
273
        PrivateUrlUser privateUrlUser = privateUrlSvc.getPrivateUrlUserFromToken(requestApiKey);
1✔
274
        if (privateUrlUser != null) {
1✔
275
            return privateUrlUser;
×
276
        }
277
        return findAuthenticatedUserOrDie(requestApiKey);
1✔
278
    }
279

280
    /**
281
     * Finds the authenticated user, based on (in order):
282
     * <ol>
283
     * <li>The key in the HTTP header {@link #DATAVERSE_KEY_HEADER_NAME}</li>
284
     * <li>The key in the query parameter {@code key}
285
     * </ol>
286
     * <p>
287
     * If no user is found, throws a wrapped bad api key (HTTP UNAUTHORIZED) response.
288
     *
289
     * @return The authenticated user which owns the passed api key
290
     * @throws edu.harvard.iq.dataverse.api.AbstractApiBean.WrappedResponse in case said user is not found.
291
     */
292
    protected AuthenticatedUser findAuthenticatedUserOrDie() throws WrappedResponse {
293
        return findAuthenticatedUserOrDie(getRequestApiKey());
1✔
294
    }
295

296
    protected AuthenticatedUser findSuperuserOrDie() throws WrappedResponse {
297
        AuthenticatedUser user = findAuthenticatedUserOrDie();
1✔
298
        if (!user.isSuperuser()) {
1✔
299
            throw new WrappedResponse(forbidden("This API call can be used by superusers only"));
×
300
        }
301
        return user;
1✔
302
    }
303

304
    private AuthenticatedUser findAuthenticatedUserOrDie(String key) throws WrappedResponse {
305
        AuthenticatedUser authUser = authSvc.lookupUser(key);
1✔
306
        if (authUser != null) {
1✔
307
            if (!systemConfig.isReadonlyMode()) {
1✔
308
                authUser = userSvc.updateLastApiUseTime(authUser);
×
309
            }
310

311
            return authUser;
1✔
312
        }
313
        throw new WrappedResponse(badApiKey(key));
1✔
314
    }
315

316
    protected AuthenticatedUser findSuperuserWithSessionFallbackOrDie() throws WrappedResponse {
317
        try {
NEW
318
            return findSuperuserOrDie();
×
NEW
319
        } catch (WrappedResponse e) {
×
NEW
320
            User user = getSessionUserWithGuestFallback();
×
NEW
321
            if (user.isAuthenticated() && user.isSuperuser() && user instanceof AuthenticatedUser) {
×
NEW
322
                return (AuthenticatedUser) user;
×
323
            }
NEW
324
            throw e;
×
325
        }
326
    }
327

328
    protected User getSessionUserWithGuestFallback() {
329
        return Option.of(session)
1✔
330
                .map(DataverseSession::getUser)
1✔
331
                .peek(user -> logger.log(Level.FINE, "User associated with the session is {0}", user.getIdentifier()))
1✔
332
                .getOrElse(() -> {
1✔
NEW
333
                    logger.fine("Session is null. Assuming guest user");
×
NEW
334
                    return GuestUser.get();
×
335
                });
336
    }
337

338
    protected Dataverse findDataverseOrDie(String dvIdtf) throws WrappedResponse {
339
        Dataverse dv = isNumeric(dvIdtf) ? dataverseSvc.find(Long.parseLong(dvIdtf))
×
340
                : dataverseSvc.findByAlias(dvIdtf);
×
341
        if (dv == null) {
×
342
            throw new WrappedResponse(error(Response.Status.NOT_FOUND, "Can't find dataverse with identifier='" + dvIdtf + "'"));
×
343
        }
344
        return dv;
×
345
    }
346

347
    protected DataverseLinkingDataverse findDataverseLinkingDataverseOrDie(String dataverseId, String linkedDataverseId) throws WrappedResponse {
348
        DataverseLinkingDataverse dvld;
349
        Dataverse dataverse = findDataverseOrDie(dataverseId);
×
350
        Dataverse linkedDataverse = findDataverseOrDie(linkedDataverseId);
×
351
        try {
352
            dvld = dvLinkingService.findDataverseLinkingDataverse(dataverse.getId(), linkedDataverse.getId());
×
353
            if (dvld == null) {
×
354
                throw new WrappedResponse(notFound(BundleUtil.getStringFromBundle("find.dataverselinking.error.not.found.ids", dataverseId, linkedDataverseId)));
×
355
            }
356
            return dvld;
×
357
        } catch (NumberFormatException nfe) {
×
358
            throw new WrappedResponse(
×
359
                    badRequest(BundleUtil.getStringFromBundle("find.dataverselinking.error.not.found.bad.ids", dataverseId, linkedDataverseId)));
×
360
        }
361
    }
362

363
    protected Dataset findDatasetOrDie(String id) throws WrappedResponse {
364
        Dataset dataset;
365
        if (id.equals(PERSISTENT_ID_KEY)) {
1✔
366
            String persistentId = getRequestParameter(PERSISTENT_ID_KEY.substring(1));
×
367
            if (persistentId == null) {
×
368
                throw new WrappedResponse(
×
369
                        badRequest(BundleUtil.getStringFromBundle("find.dataset.error.dataset_id_is_null", PERSISTENT_ID_KEY.substring(1))));
×
370
            }
371
            dataset = datasetSvc.findByGlobalId(persistentId);
×
372
            if (dataset == null) {
×
373
                throw new WrappedResponse(notFound(BundleUtil.getStringFromBundle("find.dataset.error.dataset.not.found.persistentId", persistentId)));
×
374
            }
375
            return dataset;
×
376

377
        } else {
378
            try {
379
                dataset = datasetSvc.find(Long.parseLong(id));
1✔
380
                if (dataset == null) {
1✔
381
                    throw new WrappedResponse(notFound(BundleUtil.getStringFromBundle("find.dataset.error.dataset.not.found.id", id)));
×
382
                }
383
                return dataset;
1✔
384
            } catch (NumberFormatException nfe) {
×
385
                throw new WrappedResponse(
×
386
                        badRequest(BundleUtil.getStringFromBundle("find.dataset.error.dataset.not.found.bad.id", id)));
×
387
            }
388
        }
389
    }
390

391
    protected DataFile findDataFileOrDie(String id) throws WrappedResponse {
392
        DataFile datafile;
393
        if (id.equals(PERSISTENT_ID_KEY)) {
×
394
            String persistentId = getRequestParameter(PERSISTENT_ID_KEY.substring(1));
×
395
            if (persistentId == null) {
×
396
                throw new WrappedResponse(
×
397
                        badRequest(BundleUtil.getStringFromBundle("find.dataset.error.dataset_id_is_null", PERSISTENT_ID_KEY.substring(1))));
×
398
            }
399
            datafile = fileService.findByGlobalId(persistentId);
×
400
            if (datafile == null) {
×
401
                throw new WrappedResponse(notFound(BundleUtil.getStringFromBundle("find.datafile.error.dataset.not.found.persistentId", persistentId)));
×
402
            }
403
            return datafile;
×
404
        } else {
405
            try {
406
                datafile = fileService.find(Long.parseLong(id));
×
407
                if (datafile == null) {
×
408
                    throw new WrappedResponse(notFound(BundleUtil.getStringFromBundle("find.datafile.error.datafile.not.found.id", id)));
×
409
                }
410
                return datafile;
×
411
            } catch (NumberFormatException nfe) {
×
412
                throw new WrappedResponse(
×
413
                        badRequest(BundleUtil.getStringFromBundle("find.datafile.error.datafile.not.found.bad.id", id)));
×
414
            }
415
        }
416
    }
417

418
    protected DatasetLinkingDataverse findDatasetLinkingDataverseOrDie(String datasetId, String linkingDataverseId) throws WrappedResponse {
419
        DatasetLinkingDataverse dsld;
420
        Dataverse linkingDataverse = findDataverseOrDie(linkingDataverseId);
×
421

422
        if (datasetId.equals(PERSISTENT_ID_KEY)) {
×
423
            String persistentId = getRequestParameter(PERSISTENT_ID_KEY.substring(1));
×
424
            if (persistentId == null) {
×
425
                throw new WrappedResponse(
×
426
                        badRequest(BundleUtil.getStringFromBundle("find.dataset.error.dataset_id_is_null", PERSISTENT_ID_KEY.substring(1))));
×
427
            }
428

429
            Dataset dataset = datasetSvc.findByGlobalId(persistentId);
×
430
            if (dataset == null) {
×
431
                throw new WrappedResponse(notFound(BundleUtil.getStringFromBundle("find.dataset.error.dataset.not.found.persistentId", persistentId)));
×
432
            }
433
            datasetId = dataset.getId().toString();
×
434
        }
435
        try {
436
            dsld = dsLinkingService.findDatasetLinkingDataverse(Long.parseLong(datasetId), linkingDataverse.getId());
×
437
            if (dsld == null) {
×
438
                throw new WrappedResponse(notFound(BundleUtil.getStringFromBundle("find.datasetlinking.error.not.found.ids", datasetId, linkingDataverse.getId().toString())));
×
439
            }
440
            return dsld;
×
441
        } catch (NumberFormatException nfe) {
×
442
            throw new WrappedResponse(
×
443
                    badRequest(BundleUtil.getStringFromBundle("find.datasetlinking.error.not.found.bad.ids", datasetId, linkingDataverse.getId().toString())));
×
444
        }
445
    }
446

447
    protected DataverseRequest createDataverseRequest(User u) {
448
        return new DataverseRequest(u, httpRequest);
1✔
449
    }
450

451
    /* =================== *\
452
     *  Command Execution  *
453
    \* =================== */
454

455
    /**
456
     * Executes a command, and returns the appropriate result/HTTP response.
457
     *
458
     * @param <T> Return type for the command
459
     * @param cmd The command to execute.
460
     * @return Value from the command
461
     * @throws edu.harvard.iq.dataverse.api.AbstractApiBean.WrappedResponse Unwrap and return.
462
     * @see #response(java.util.concurrent.Callable)
463
     */
464
    protected <T> T execCommand(Command<T> cmd) throws WrappedResponse {
465
        try {
466
            return engineSvc.submit(cmd);
×
467

468
        } catch (IllegalCommandException ex) {
×
469
            throw new WrappedResponse(ex, forbidden(ex.getMessage()));
×
470
        } catch (PermissionException ex) {
×
471
            /**
472
             * @todo Is there any harm in exposing ex.getLocalizedMessage()?
473
             * There's valuable information in there that can help people reason
474
             * about permissions!
475
             */
476
            String message = "User " + cmd.getRequest().getUser().getIdentifier() + " is not permitted to perform requested action.";
×
477
            if (systemConfig.isUnconfirmedMailRestrictionModeEnabled()) {
×
478
                message += " Alternatively user has not confirmed e-mail yet.";
×
479
            }
480
            throw new WrappedResponse(error(Response.Status.UNAUTHORIZED, message));
×
481

482
        } catch (CommandException ex) {
×
483
            Logger.getLogger(AbstractApiBean.class.getName()).log(Level.SEVERE, "Error while executing command " + cmd, ex);
×
484
            throw new WrappedResponse(ex, error(Status.INTERNAL_SERVER_ERROR, ex.getMessage()));
×
485
        }
486
    }
487

488
    /**
489
     * A syntactically nicer way of using {@link #execCommand(edu.harvard.iq.dataverse.engine.command.Command)}.
490
     *
491
     * @param hdl The block to run.
492
     * @return HTTP Response appropriate for the way {@code hdl} executed.
493
     */
494
    protected Response response(Callable<Response> hdl) {
495
        try {
496
            return hdl.call();
×
497
        } catch (WrappedResponse rr) {
×
498
            return rr.getResponse();
×
499
        } catch (Exception ex) {
×
500
            String incidentId = UUID.randomUUID().toString();
×
501
            logger.log(Level.SEVERE, "API internal error " + incidentId + ": " + ex.getMessage(), ex);
×
502
            return Response.status(500)
×
503
                    .entity(Json.createObjectBuilder()
×
504
                                    .add("status", "ERROR")
×
505
                                    .add("code", 500)
×
506
                                    .add("message", "Internal server error. More details available at the server logs.")
×
507
                                    .add("incidentId", incidentId)
×
508
                                    .build())
×
509
                    .type("application/json").build();
×
510
        }
511
    }
512

513
    /**
514
     * The preferred way of handling a request that requires a user. The system
515
     * looks for the user and, if found, handles it to the handler for doing the
516
     * actual work.
517
     * <p>
518
     * This is a relatively secure way to handle things, since if the user is not
519
     * found, the response is about the bad API key, rather than something else
520
     * (say, 404 NOT FOUND which leaks information about the existence of the
521
     * sought object).
522
     *
523
     * @param hdl handling code block.
524
     * @return HTTP Response appropriate for the way {@code hdl} executed.
525
     */
526
    protected Response response(DataverseRequestHandler hdl) {
527
        try {
528
            return hdl.handle(createDataverseRequest(findUserOrDie()));
1✔
529
        } catch (WrappedResponse rr) {
×
530
            return rr.getResponse();
×
531
        } catch (Exception ex) {
×
532
            String incidentId = UUID.randomUUID().toString();
×
533
            logger.log(Level.SEVERE, "API internal error " + incidentId + ": " + ex.getMessage(), ex);
×
534
            return Response.status(500)
×
535
                    .entity(Json.createObjectBuilder()
×
536
                                    .add("status", "ERROR")
×
537
                                    .add("code", 500)
×
538
                                    .add("message", "Internal server error. More details available at the server logs.")
×
539
                                    .add("incidentId", incidentId)
×
540
                                    .build())
×
541
                    .type("application/json").build();
×
542
        }
543
    }
544

545
    /* ====================== *\
546
     *  HTTP Response methods *
547
    \* ====================== */
548

549
    protected Response ok(JsonValue value) {
550
        return Response.ok(Json.createObjectBuilder()
×
551
                .add("status", STATUS_OK)
×
552
                .add("data", value).build())
×
553
                .type(MediaType.APPLICATION_JSON_TYPE)
×
554
                .build();
×
555
    }
556

557
    protected Response ok(JsonObjectBuilder bld) {
558
        return ok(bld.build());
×
559
    }
560

561
    protected Response ok(JsonArrayBuilder bld) {
562
        return ok(bld.build());
×
563
    }
564

565
    protected Response ok(String msg) {
566
        return ok(Json.createObjectBuilder().add("message", msg));
×
567
    }
568

569
    protected <T> Response ok(T objectToBeSerialized) {
570
        return ok(objectToBeSerialized, null);
1✔
571
    }
572

573
    protected <T> Response ok(T objectToBeSerialized, String message) {
574
        ObjectMapper objectMapper = new ObjectMapper();
1✔
575

576
        ApiResponseDTO<T> response = new ApiResponseDTO<>(Status.OK, objectToBeSerialized, message);
1✔
577

578
        String serializedObj = Try.of(() -> objectMapper.writeValueAsString(response))
1✔
579
                .getOrElseThrow(throwable -> new SerializationException("There was a problem with serializing object",
1✔
580
                                                                        throwable));
581

582
       return Response.status(Status.OK)
1✔
583
                .type(MediaType.APPLICATION_JSON_TYPE)
1✔
584
                .entity(serializedObj)
1✔
585
                .build();
1✔
586
    }
587

588
    /**
589
     * @param data      Payload to return.
590
     * @param mediaType Non-JSON media type.
591
     * @return Non-JSON response, such as a shell script.
592
     */
593
    protected Response ok(String data, MediaType mediaType) {
594
        return Response.ok().entity(data).type(mediaType).build();
×
595
    }
596

597
    protected <T> Response created(String uri, T objectToBeSerialized) {
598
        ObjectMapper objectMapper = new ObjectMapper();
×
599

600
        ApiResponseDTO<T> response = new ApiResponseDTO<>(STATUS_OK, Status.CREATED.getStatusCode(), objectToBeSerialized);
×
601

602
        String serializedObj = Try.of(() -> objectMapper.writeValueAsString(response))
×
603
                .getOrElseThrow(throwable -> new SerializationException("There was a problem with serializing object",
×
604
                        throwable));
605

606
        return Response.created(URI.create(uri))
×
607
                .type(MediaType.APPLICATION_JSON_TYPE)
×
608
                .entity(serializedObj)
×
609
                .build();
×
610
    }
611

612

613
    protected Response created(String uri, JsonObjectBuilder bld) {
614
        return Response.created(URI.create(uri))
×
615
                .entity(Json.createObjectBuilder()
×
616
                                .add("status", "OK")
×
617
                                .add("data", bld).build())
×
618
                .type(MediaType.APPLICATION_JSON)
×
619
                .build();
×
620
    }
621

622
    protected <T> Response accepted(T objectToBeSerialized) {
623
        ObjectMapper objectMapper = new ObjectMapper();
×
624
        ApiResponseDTO<T> response = new ApiResponseDTO<>(STATUS_WF_IN_PROGRESS, 202, objectToBeSerialized);
×
625
        String serializedObj = Try.of(() -> objectMapper.writeValueAsString(response))
×
626
                .getOrElseThrow(throwable -> new SerializationException("There was a problem with serializing object",
×
627
                        throwable));
628
        return Response.accepted()
×
629
                .type(MediaType.APPLICATION_JSON_TYPE)
×
630
                .entity(serializedObj)
×
631
                .build();
×
632
    }
633

634
    protected Response accepted() {
635
        return Response.accepted()
×
636
                .entity(Json.createObjectBuilder()
×
637
                        .add("status", STATUS_WF_IN_PROGRESS)
×
638
                        .build())
×
639
                .build();
×
640
    }
641

642
    protected Response notFound(String msg) {
643
        return error(Status.NOT_FOUND, msg);
×
644
    }
645

646
    protected Response badRequest(String msg) {
647
        return error(Status.BAD_REQUEST, msg);
×
648
    }
649

650
    protected Response forbidden(String msg) {
651
        return error(Status.FORBIDDEN, msg);
×
652
    }
653

654
    protected Response badApiKey(String apiKey) {
655
        return error(Status.UNAUTHORIZED, apiKey != null
1✔
656
                ? "Bad api key "
657
                : "Please provide a key query parameter (?key=XXX) or via the HTTP header " + DATAVERSE_KEY_HEADER_NAME);
658
    }
659

660
    protected Response permissionError(PermissionException pe) {
661
        return permissionError(pe.getMessage());
×
662
    }
663

664
    protected Response permissionError(String message) {
665
        return unauthorized(message);
×
666
    }
667

668
    protected Response unauthorized(String message) {
669
        return error(Status.UNAUTHORIZED, message);
×
670
    }
671

672
    protected Response internalServerError(String message) {
673
        return error(Status.INTERNAL_SERVER_ERROR, message);
×
674
    }
675

676
    protected static Response error(Status status, String message) {
677
                return Response.status(status)
1✔
678
                        .entity(ApiErrorResponseDTO.errorResponse(status.getStatusCode(), message))
1✔
679
                        .type(MediaType.APPLICATION_JSON_TYPE)
1✔
680
                        .build();
1✔
681
    }
682

683
    protected Response allowCors(Response r) {
684
        r.getHeaders().add("Access-Control-Allow-Origin", "*");
1✔
685
        return r;
1✔
686
    }
687
}
688

689
class LazyRef<T> {
690
    private interface Ref<T> {
691
        T get();
692
    }
693

694
    private Ref<T> ref;
695

696
    public LazyRef(final Callable<T> initer) {
1✔
697
        ref = () -> {
1✔
698
            try {
699
                final T t = initer.call();
×
700
                ref = () -> t;
×
701
                return ref.get();
×
702
            } catch (Exception ex) {
×
703
                Logger.getLogger(LazyRef.class.getName()).log(Level.SEVERE, null, ex);
×
704
                return null;
×
705
            }
706
        };
707
    }
1✔
708

709
    public T get() {
710
        return ref.get();
×
711
    }
712
}
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