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

IQSS / dataverse / #23660

05 Nov 2024 05:11PM CUT coverage: 21.206% (-0.02%) from 21.224%
#23660

Pull #11001

github

GPortas
Changed: using JPA criteria instead of code looping for DatasetType query
Pull Request #11001: allow links between dataset types and metadata blocks

1 of 99 new or added lines in 7 files covered. (1.01%)

3 existing lines in 3 files now uncovered.

18316 of 86371 relevant lines covered (21.21%)

0.21 hits per line

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

5.26
/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java
1
package edu.harvard.iq.dataverse.api;
2

3
import edu.harvard.iq.dataverse.*;
4
import edu.harvard.iq.dataverse.api.auth.AuthRequired;
5
import edu.harvard.iq.dataverse.api.datadeposit.SwordServiceBean;
6
import edu.harvard.iq.dataverse.api.dto.*;
7
import edu.harvard.iq.dataverse.authorization.DataverseRole;
8

9
import edu.harvard.iq.dataverse.api.imports.ImportException;
10
import edu.harvard.iq.dataverse.api.imports.ImportServiceBean;
11
import edu.harvard.iq.dataverse.authorization.Permission;
12
import edu.harvard.iq.dataverse.authorization.RoleAssignee;
13
import edu.harvard.iq.dataverse.authorization.groups.impl.explicit.ExplicitGroup;
14
import edu.harvard.iq.dataverse.authorization.groups.impl.explicit.ExplicitGroupProvider;
15
import edu.harvard.iq.dataverse.authorization.groups.impl.explicit.ExplicitGroupServiceBean;
16
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
17
import edu.harvard.iq.dataverse.authorization.users.User;
18
import edu.harvard.iq.dataverse.dataset.DatasetType;
19
import edu.harvard.iq.dataverse.dataverse.DataverseUtil;
20
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
21
import edu.harvard.iq.dataverse.engine.command.impl.*;
22
import edu.harvard.iq.dataverse.pidproviders.PidProvider;
23
import edu.harvard.iq.dataverse.pidproviders.PidUtil;
24
import edu.harvard.iq.dataverse.settings.JvmSettings;
25
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
26
import edu.harvard.iq.dataverse.util.BundleUtil;
27
import edu.harvard.iq.dataverse.util.ConstraintViolationUtil;
28
import edu.harvard.iq.dataverse.util.StringUtil;
29
import static edu.harvard.iq.dataverse.util.StringUtil.nonEmpty;
30
import static edu.harvard.iq.dataverse.util.json.JsonPrinter.*;
31

32
import edu.harvard.iq.dataverse.util.json.JSONLDUtil;
33
import edu.harvard.iq.dataverse.util.json.JsonParseException;
34
import edu.harvard.iq.dataverse.util.json.JsonPrinter;
35
import edu.harvard.iq.dataverse.util.json.JsonUtil;
36

37
import java.io.StringReader;
38
import java.util.*;
39
import java.util.logging.Level;
40
import java.util.logging.Logger;
41
import jakarta.ejb.EJB;
42
import jakarta.ejb.EJBException;
43
import jakarta.ejb.Stateless;
44
import jakarta.json.*;
45
import jakarta.json.JsonValue.ValueType;
46
import jakarta.json.stream.JsonParsingException;
47
import jakarta.validation.ConstraintViolationException;
48
import jakarta.ws.rs.BadRequestException;
49
import jakarta.ws.rs.Consumes;
50
import jakarta.ws.rs.DELETE;
51
import jakarta.ws.rs.GET;
52
import jakarta.ws.rs.POST;
53
import jakarta.ws.rs.PUT;
54
import jakarta.ws.rs.Path;
55
import jakarta.ws.rs.PathParam;
56
import jakarta.ws.rs.Produces;
57
import jakarta.ws.rs.QueryParam;
58
import jakarta.ws.rs.container.ContainerRequestContext;
59
import jakarta.ws.rs.core.MediaType;
60
import jakarta.ws.rs.core.Response;
61
import jakarta.ws.rs.core.Response.Status;
62

63
import java.io.IOException;
64
import java.io.OutputStream;
65
import java.text.MessageFormat;
66
import java.text.SimpleDateFormat;
67
import java.util.stream.Collectors;
68
import jakarta.servlet.http.HttpServletResponse;
69
import jakarta.ws.rs.WebApplicationException;
70
import jakarta.ws.rs.core.Context;
71
import jakarta.ws.rs.core.StreamingOutput;
72
import javax.xml.stream.XMLStreamException;
73

74
/**
75
 * A REST API for dataverses.
76
 *
77
 * @author michael
78
 */
79
@Stateless
80
@Path("dataverses")
81
public class Dataverses extends AbstractApiBean {
1✔
82

83
    private static final Logger logger = Logger.getLogger(Dataverses.class.getCanonicalName());
1✔
84
    private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss");
1✔
85

86
    @EJB
87
    ExplicitGroupServiceBean explicitGroupSvc;
88

89
    @EJB
90
    ImportServiceBean importService;
91
    
92
    @EJB
93
    SettingsServiceBean settingsService;
94
    
95
    @EJB
96
    GuestbookResponseServiceBean guestbookResponseService;
97
    
98
    @EJB
99
    GuestbookServiceBean guestbookService;
100
    
101
    @EJB
102
    DataverseServiceBean dataverseService;
103

104
    @EJB
105
    DataverseLinkingServiceBean linkingService;
106

107
    @EJB
108
    FeaturedDataverseServiceBean featuredDataverseService;
109

110
    @EJB
111
    SwordServiceBean swordService;
112

113
    @EJB
114
    PermissionServiceBean permissionService;
115
    
116
    @POST
117
    @AuthRequired
118
    public Response addRoot(@Context ContainerRequestContext crc, String body) {
119
        logger.info("Creating root dataverse");
×
120
        return addDataverse(crc, body, "");
×
121
    }
122

123
    @POST
124
    @AuthRequired
125
    @Path("{identifier}")
126
    public Response addDataverse(@Context ContainerRequestContext crc, String body, @PathParam("identifier") String parentIdtf) {
127
        Dataverse newDataverse;
128
        try {
129
            newDataverse = parseAndValidateAddDataverseRequestBody(body);
×
130
        } catch (JsonParsingException jpe) {
×
131
            return error(Status.BAD_REQUEST, MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.create.error.jsonparse"), jpe.getMessage()));
×
132
        } catch (JsonParseException ex) {
×
133
            return error(Status.BAD_REQUEST, MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.create.error.jsonparsetodataverse"), ex.getMessage()));
×
134
        }
×
135

136
        try {
137
            List<DataverseFieldTypeInputLevel> inputLevels = parseInputLevels(body, newDataverse);
×
138
            List<MetadataBlock> metadataBlocks = parseMetadataBlocks(body);
×
139
            List<DatasetFieldType> facets = parseFacets(body);
×
140

141
            if (!parentIdtf.isEmpty()) {
×
142
                Dataverse owner = findDataverseOrDie(parentIdtf);
×
143
                newDataverse.setOwner(owner);
×
144
            }
145

146
            AuthenticatedUser u = getRequestAuthenticatedUserOrDie(crc);
×
147
            newDataverse = execCommand(new CreateDataverseCommand(newDataverse, createDataverseRequest(u), facets, inputLevels, metadataBlocks));
×
148
            return created("/dataverses/" + newDataverse.getAlias(), json(newDataverse));
×
149

150
        } catch (WrappedResponse ww) {
×
151
            return handleWrappedResponse(ww);
×
152
        } catch (EJBException ex) {
×
153
            return handleEJBException(ex, "Error creating dataverse.");
×
154
        } catch (Exception ex) {
×
155
            logger.log(Level.SEVERE, "Error creating dataverse", ex);
×
156
            return error(Response.Status.INTERNAL_SERVER_ERROR, "Error creating dataverse: " + ex.getMessage());
×
157
        }
158
    }
159

160
    private Dataverse parseAndValidateAddDataverseRequestBody(String body) throws JsonParsingException, JsonParseException {
161
        try {
162
            JsonObject addDataverseJson = JsonUtil.getJsonObject(body);
×
163
            return jsonParser().parseDataverse(addDataverseJson);
×
164
        } catch (JsonParsingException jpe) {
×
165
            logger.log(Level.SEVERE, "Json: {0}", body);
×
166
            throw jpe;
×
167
        } catch (JsonParseException ex) {
×
168
            logger.log(Level.SEVERE, "Error parsing dataverse from json: " + ex.getMessage(), ex);
×
169
            throw ex;
×
170
        }
171
    }
172

173
    @PUT
174
    @AuthRequired
175
    @Path("{identifier}")
176
    public Response updateDataverse(@Context ContainerRequestContext crc, String body, @PathParam("identifier") String identifier) {
177
        Dataverse dataverse;
178
        try {
179
            dataverse = findDataverseOrDie(identifier);
×
180
        } catch (WrappedResponse e) {
×
181
            return e.getResponse();
×
182
        }
×
183

184
        DataverseDTO updatedDataverseDTO;
185
        try {
186
            updatedDataverseDTO = parseAndValidateUpdateDataverseRequestBody(body);
×
187
        } catch (JsonParsingException jpe) {
×
188
            return error(Status.BAD_REQUEST, MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.create.error.jsonparse"), jpe.getMessage()));
×
189
        } catch (JsonParseException ex) {
×
190
            return error(Status.BAD_REQUEST, MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.create.error.jsonparsetodataverse"), ex.getMessage()));
×
191
        }
×
192

193
        try {
194
            List<DataverseFieldTypeInputLevel> inputLevels = parseInputLevels(body, dataverse);
×
195
            List<MetadataBlock> metadataBlocks = parseMetadataBlocks(body);
×
196
            List<DatasetFieldType> facets = parseFacets(body);
×
197

198
            AuthenticatedUser u = getRequestAuthenticatedUserOrDie(crc);
×
199
            dataverse = execCommand(new UpdateDataverseCommand(dataverse, facets, null, createDataverseRequest(u), inputLevels, metadataBlocks, updatedDataverseDTO));
×
200
            return ok(json(dataverse));
×
201

202
        } catch (WrappedResponse ww) {
×
203
            return handleWrappedResponse(ww);
×
204
        } catch (EJBException ex) {
×
205
            return handleEJBException(ex, "Error updating dataverse.");
×
206
        } catch (Exception ex) {
×
207
            logger.log(Level.SEVERE, "Error updating dataverse", ex);
×
208
            return error(Response.Status.INTERNAL_SERVER_ERROR, "Error updating dataverse: " + ex.getMessage());
×
209
        }
210
    }
211

212
    private DataverseDTO parseAndValidateUpdateDataverseRequestBody(String body) throws JsonParsingException, JsonParseException {
213
        try {
214
            JsonObject updateDataverseJson = JsonUtil.getJsonObject(body);
×
215
            return jsonParser().parseDataverseDTO(updateDataverseJson);
×
216
        } catch (JsonParsingException jpe) {
×
217
            logger.log(Level.SEVERE, "Json: {0}", body);
×
218
            throw jpe;
×
219
        } catch (JsonParseException ex) {
×
220
            logger.log(Level.SEVERE, "Error parsing DataverseDTO from json: " + ex.getMessage(), ex);
×
221
            throw ex;
×
222
        }
223
    }
224

225
    private List<DataverseFieldTypeInputLevel> parseInputLevels(String body, Dataverse dataverse) throws WrappedResponse {
226
        JsonObject metadataBlocksJson = getMetadataBlocksJson(body);
×
227
        if (metadataBlocksJson == null) {
×
228
            return null;
×
229
        }
230
        JsonArray inputLevelsArray = metadataBlocksJson.getJsonArray("inputLevels");
×
231
        return inputLevelsArray != null ? parseInputLevels(inputLevelsArray, dataverse) : null;
×
232
    }
233

234
    private List<MetadataBlock> parseMetadataBlocks(String body) throws WrappedResponse {
235
        JsonObject metadataBlocksJson = getMetadataBlocksJson(body);
×
236
        if (metadataBlocksJson == null) {
×
237
            return null;
×
238
        }
239
        JsonArray metadataBlocksArray = metadataBlocksJson.getJsonArray("metadataBlockNames");
×
240
        return metadataBlocksArray != null ? parseNewDataverseMetadataBlocks(metadataBlocksArray) : null;
×
241
    }
242

243
    private List<DatasetFieldType> parseFacets(String body) throws WrappedResponse {
244
        JsonObject metadataBlocksJson = getMetadataBlocksJson(body);
×
245
        if (metadataBlocksJson == null) {
×
246
            return null;
×
247
        }
248
        JsonArray facetsArray = metadataBlocksJson.getJsonArray("facetIds");
×
249
        return facetsArray != null ? parseFacets(facetsArray) : null;
×
250
    }
251

252
    private JsonObject getMetadataBlocksJson(String body) {
253
        JsonObject dataverseJson = JsonUtil.getJsonObject(body);
×
254
        return dataverseJson.getJsonObject("metadataBlocks");
×
255
    }
256

257
    private Response handleWrappedResponse(WrappedResponse ww) {
258
        String error = ConstraintViolationUtil.getErrorStringForConstraintViolations(ww.getCause());
×
259
        if (!error.isEmpty()) {
×
260
            logger.log(Level.INFO, error);
×
261
            return ww.refineResponse(error);
×
262
        }
263
        return ww.getResponse();
×
264
    }
265

266
    private Response handleEJBException(EJBException ex, String action) {
267
        Throwable cause = ex;
×
268
        StringBuilder sb = new StringBuilder();
×
269
        sb.append(action);
×
270
        while (cause.getCause() != null) {
×
271
            cause = cause.getCause();
×
272
            if (cause instanceof ConstraintViolationException) {
×
273
                sb.append(ConstraintViolationUtil.getErrorStringForConstraintViolations(cause));
×
274
            }
275
        }
276
        logger.log(Level.SEVERE, sb.toString());
×
277
        return error(Response.Status.INTERNAL_SERVER_ERROR, sb.toString());
×
278
    }
279

280
    private List<MetadataBlock> parseNewDataverseMetadataBlocks(JsonArray metadataBlockNamesArray) throws WrappedResponse {
281
        List<MetadataBlock> selectedMetadataBlocks = new ArrayList<>();
×
282
        for (JsonString metadataBlockName : metadataBlockNamesArray.getValuesAs(JsonString.class)) {
×
283
            MetadataBlock metadataBlock = metadataBlockSvc.findByName(metadataBlockName.getString());
×
284
            if (metadataBlock == null) {
×
285
                String errorMessage = MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.metadatablocks.error.invalidmetadatablockname"), metadataBlockName);
×
286
                throw new WrappedResponse(badRequest(errorMessage));
×
287
            }
288
            selectedMetadataBlocks.add(metadataBlock);
×
289
        }
×
290

291
        return selectedMetadataBlocks;
×
292
    }
293

294
    @POST
295
    @AuthRequired
296
    @Path("{identifier}/validateDatasetJson")
297
    @Consumes("application/json")
298
    public Response validateDatasetJson(@Context ContainerRequestContext crc, String body, @PathParam("identifier") String idtf) {
299
        User u = getRequestUser(crc);
×
300
        try {
301
            String validationMessage = execCommand(new ValidateDatasetJsonCommand(createDataverseRequest(u), findDataverseOrDie(idtf), body));
×
302
            return ok(validationMessage);
×
303
        } catch (WrappedResponse ex) {
×
304
            Logger.getLogger(Dataverses.class.getName()).log(Level.SEVERE, null, ex);
×
305
            return ex.getResponse();
×
306
        }
307
    }
308
    
309
    @GET
310
    @AuthRequired
311
    @Path("{identifier}/datasetSchema")
312
    @Produces(MediaType.APPLICATION_JSON)
313
    public Response getDatasetSchema(@Context ContainerRequestContext crc, @PathParam("identifier") String idtf) {
314
        User u = getRequestUser(crc);
×
315

316
        try {
317
            String datasetSchema = execCommand(new GetDatasetSchemaCommand(createDataverseRequest(u), findDataverseOrDie(idtf)));
×
318
            JsonObject jsonObject = JsonUtil.getJsonObject(datasetSchema);
×
319
            return Response.ok(jsonObject).build();
×
320
        } catch (WrappedResponse ex) {
×
321
            Logger.getLogger(Dataverses.class.getName()).log(Level.SEVERE, null, ex);
×
322
            return ex.getResponse();
×
323
        }
324
    }
325
            
326
    
327

328
    @POST
329
    @AuthRequired
330
    @Path("{identifier}/datasets")
331
    @Consumes("application/json")
332
    public Response createDataset(@Context ContainerRequestContext crc, String jsonBody, @PathParam("identifier") String parentIdtf, @QueryParam("doNotValidate") String doNotValidateParam) {
333
        try {
334
            logger.fine("Json is: " + jsonBody);
×
335
            User u = getRequestUser(crc);
×
336
            Dataverse owner = findDataverseOrDie(parentIdtf);
×
337
            Dataset ds = parseDataset(jsonBody);
×
338
            ds.setOwner(owner);
×
339
            // Will make validation happen always except for the (rare) occasion of all three conditions are true
340
            boolean validate = ! ( u.isAuthenticated() && StringUtil.isTrue(doNotValidateParam) &&
×
341
                JvmSettings.API_ALLOW_INCOMPLETE_METADATA.lookupOptional(Boolean.class).orElse(false) );
×
342

343
            if (ds.getVersions().isEmpty()) {
×
344
                return badRequest(BundleUtil.getStringFromBundle("dataverses.api.create.dataset.error.mustIncludeVersion"));
×
345
            }
346
            
347
            if (!ds.getFiles().isEmpty() && !u.isSuperuser()){
×
348
                return badRequest(BundleUtil.getStringFromBundle("dataverses.api.create.dataset.error.superuserFiles"));
×
349
            }
350

351
            //Throw BadRequestException if metadataLanguage isn't compatible with setting
352
            DataverseUtil.checkMetadataLangauge(ds, owner, settingsService.getBaseMetadataLanguageMap(null, true));
×
353

354
            // clean possible version metadata
355
            DatasetVersion version = ds.getVersions().get(0);
×
356

357
            if (!validate && (version.getDatasetAuthors().isEmpty() || version.getDatasetAuthors().stream().anyMatch(a -> a.getName() == null || a.getName().isEmpty()))) {
×
358
                return badRequest(BundleUtil.getStringFromBundle("dataverses.api.create.dataset.error.mustIncludeAuthorName"));
×
359
            }
360

361
            version.setMinorVersionNumber(null);
×
362
            version.setVersionNumber(null);
×
363
            version.setVersionState(DatasetVersion.VersionState.DRAFT);
×
364
            version.getTermsOfUseAndAccess().setFileAccessRequest(true);
×
365
            version.getTermsOfUseAndAccess().setDatasetVersion(version);
×
366

367
            ds.setAuthority(null);
×
368
            ds.setIdentifier(null);
×
369
            ds.setProtocol(null);
×
370
            ds.setGlobalIdCreateTime(null);
×
371
            Dataset managedDs = null;
×
372
            try {
373
                managedDs = execCommand(new CreateNewDatasetCommand(ds, createDataverseRequest(u), null, validate));
×
374
            } catch (WrappedResponse ww) {
×
375
                Throwable cause = ww.getCause();
×
376
                StringBuilder sb = new StringBuilder();
×
377
                if (cause == null) {
×
378
                    return ww.refineResponse("cause was null!");
×
379
                }
380
                while (cause.getCause() != null) {
×
381
                    cause = cause.getCause();
×
382
                    if (cause instanceof ConstraintViolationException) {
×
383
                        sb.append(ConstraintViolationUtil.getErrorStringForConstraintViolations(cause));
×
384
                    }
385
                }
386
                String error = sb.toString();
×
387
                if (!error.isEmpty()) {
×
388
                    logger.log(Level.INFO, error);
×
389
                    return ww.refineResponse(error);
×
390
                }
391
                return ww.getResponse();
×
392
            }
×
393

394
            return created("/datasets/" + managedDs.getId(),
×
395
                    Json.createObjectBuilder()
×
396
                            .add("id", managedDs.getId())
×
397
                            .add("persistentId", managedDs.getGlobalId().asString())
×
398
            );
399

400
        } catch (WrappedResponse ex) {
×
401
            return ex.getResponse();
×
402
        }
403
    }
404
    
405
    @POST
406
    @AuthRequired
407
    @Path("{identifier}/datasets")
408
    @Consumes("application/ld+json, application/json-ld")
409
    public Response createDatasetFromJsonLd(@Context ContainerRequestContext crc, String jsonLDBody, @PathParam("identifier") String parentIdtf) {
410
        try {
411
            User u = getRequestUser(crc);
×
412
            Dataverse owner = findDataverseOrDie(parentIdtf);
×
413
            Dataset ds = new Dataset();
×
414

415
            ds.setOwner(owner);
×
416
            ds = JSONLDUtil.updateDatasetMDFromJsonLD(ds, jsonLDBody, metadataBlockSvc, datasetFieldSvc, false, false, licenseSvc, datasetTypeSvc);
×
417
            
418
            ds.setOwner(owner);
×
419

420
            // clean possible dataset/version metadata
421
            DatasetVersion version = ds.getVersions().get(0);
×
422
            version.setMinorVersionNumber(null);
×
423
            version.setVersionNumber(null);
×
424
            version.setVersionState(DatasetVersion.VersionState.DRAFT);
×
425
            version.getTermsOfUseAndAccess().setFileAccessRequest(true);
×
426
            version.getTermsOfUseAndAccess().setDatasetVersion(version);
×
427

428
            ds.setAuthority(null);
×
429
            ds.setIdentifier(null);
×
430
            ds.setProtocol(null);
×
431
            ds.setGlobalIdCreateTime(null);
×
432
            
433
            //Throw BadRequestException if metadataLanguage isn't compatible with setting
434
            DataverseUtil.checkMetadataLangauge(ds, owner, settingsService.getBaseMetadataLanguageMap(null, true));
×
435

436
            Dataset managedDs = execCommand(new CreateNewDatasetCommand(ds, createDataverseRequest(u)));
×
437
            return created("/datasets/" + managedDs.getId(),
×
438
                    Json.createObjectBuilder()
×
439
                            .add("id", managedDs.getId())
×
440
                            .add("persistentId", managedDs.getGlobalId().asString())
×
441
            );
442

443
        } catch (WrappedResponse ex) {
×
444
            return ex.getResponse();
×
445
        } catch (Exception ex) {
×
446
            return error(Status.BAD_REQUEST, ex.getLocalizedMessage());
×
447
        }
448
    }
449

450
    @POST
451
    @AuthRequired
452
    @Path("{identifier}/datasets/:import")
453
    public Response importDataset(@Context ContainerRequestContext crc, String jsonBody, @PathParam("identifier") String parentIdtf, @QueryParam("pid") String pidParam, @QueryParam("release") String releaseParam) {
454
        try {
455
            User u = getRequestUser(crc);
×
456
            if (!u.isSuperuser()) {
×
457
                return error(Status.FORBIDDEN, "Not a superuser");
×
458
            }
459
            Dataverse owner = findDataverseOrDie(parentIdtf);
×
460
            Dataset ds = parseDataset(jsonBody);
×
461
            ds.setOwner(owner);
×
462

463
            if (ds.getVersions().isEmpty()) {
×
464
                return badRequest("Supplied json must contain a single dataset version.");
×
465
            }
466

467
            //Throw BadRequestException if metadataLanguage isn't compatible with setting
468
            DataverseUtil.checkMetadataLangauge(ds, owner, settingsService.getBaseMetadataLanguageMap(null, true));
×
469

470
            DatasetVersion version = ds.getVersions().get(0);
×
471
            if (version.getVersionState() == null) {
×
472
                version.setVersionState(DatasetVersion.VersionState.DRAFT);
×
473
            }
474

475
            if (nonEmpty(pidParam)) {
×
476
                if (!GlobalId.verifyImportCharacters(pidParam)) {
×
477
                    return badRequest("PID parameter contains characters that are not allowed by the Dataverse application. On import, the PID must only contain characters specified in this regex: " + BundleUtil.getStringFromBundle("pid.allowedCharacters"));
×
478
                }
479
                Optional<GlobalId> maybePid = PidProvider.parse(pidParam);
×
480
                if (maybePid.isPresent()) {
×
481
                    ds.setGlobalId(maybePid.get());
×
482
                } else {
483
                    // unparsable PID passed. Terminate.
484
                    return badRequest("Cannot parse the PID parameter '" + pidParam + "'. Make sure it is in valid form - see Dataverse Native API documentation.");
×
485
                }
486
            }
487

488
            if (ds.getIdentifier() == null) {
×
489
                return badRequest("Please provide a persistent identifier, either by including it in the JSON, or by using the pid query parameter.");
×
490
            }
491

492
            PidProvider pidProvider = PidUtil.getPidProvider(ds.getGlobalId().getProviderId());
×
493
            if (pidProvider == null || !pidProvider.canManagePID()) {
×
494
                return badRequest("Cannot import a dataset that has a PID that doesn't match the server's settings");
×
495
            }
496

497
            boolean shouldRelease = StringUtil.isTrue(releaseParam);
×
498
            DataverseRequest request = createDataverseRequest(u);
×
499

500
            if (shouldRelease) {
×
501
                DatasetVersion latestVersion = ds.getLatestVersion();
×
502
                latestVersion.setVersionState(DatasetVersion.VersionState.RELEASED);
×
503
                latestVersion.setVersionNumber(1l);
×
504
                latestVersion.setMinorVersionNumber(0l);
×
505
                if (latestVersion.getCreateTime() != null) {
×
506
                    latestVersion.setCreateTime(new Date());
×
507
                }
508
                if (latestVersion.getLastUpdateTime() != null) {
×
509
                    latestVersion.setLastUpdateTime(new Date());
×
510
                }
511
            }
512

513
            Dataset managedDs = execCommand(new ImportDatasetCommand(ds, request));
×
514
            JsonObjectBuilder responseBld = Json.createObjectBuilder()
×
515
                    .add("id", managedDs.getId())
×
516
                    .add("persistentId", managedDs.getGlobalId().asString());
×
517

518
            if (shouldRelease) {
×
519
                PublishDatasetResult res = execCommand(new PublishDatasetCommand(managedDs, request, false, shouldRelease));
×
520
                responseBld.add("releaseCompleted", res.isCompleted());
×
521
            }
522

523
            return created("/datasets/" + managedDs.getId(), responseBld);
×
524

525
        } catch (WrappedResponse ex) {
×
526
            return ex.getResponse();
×
527
        }
528
    }
529

530
    // TODO decide if I merge importddi with import just below (xml and json on same api, instead of 2 api)
531
    @POST
532
    @AuthRequired
533
    @Path("{identifier}/datasets/:importddi")
534
    public Response importDatasetDdi(@Context ContainerRequestContext crc, String xml, @PathParam("identifier") String parentIdtf, @QueryParam("pid") String pidParam, @QueryParam("release") String releaseParam) {
535
        try {
536
            User u = getRequestUser(crc);
×
537
            if (!u.isSuperuser()) {
×
538
                return error(Status.FORBIDDEN, "Not a superuser");
×
539
            }
540
            Dataverse owner = findDataverseOrDie(parentIdtf);
×
541
            Dataset ds = null;
×
542
            try {
543
                ds = jsonParser().parseDataset(importService.ddiToJson(xml));
×
544
                DataverseUtil.checkMetadataLangauge(ds, owner, settingsService.getBaseMetadataLanguageMap(null, true));
×
545
            } catch (JsonParseException jpe) {
×
546
                return badRequest("Error parsing data as Json: "+jpe.getMessage());
×
547
            } catch (ImportException e) {
×
548
                return badRequest("Invalid DOI found in the XML: "+e.getMessage());
×
549
            } catch (XMLStreamException e) {
×
550
                return badRequest("Invalid file content: "+e.getMessage());
×
551
            }
×
552

553
            swordService.addDatasetSubjectIfMissing(ds.getLatestVersion());
×
554

555
            ds.setOwner(owner);
×
556
            if (nonEmpty(pidParam)) {
×
557
                if (!GlobalId.verifyImportCharacters(pidParam)) {
×
558
                    return badRequest("PID parameter contains characters that are not allowed by the Dataverse application. On import, the PID must only contain characters specified in this regex: " + BundleUtil.getStringFromBundle("pid.allowedCharacters"));
×
559
                }
560
                Optional<GlobalId> maybePid = PidProvider.parse(pidParam);
×
561
                if (maybePid.isPresent()) {
×
562
                    ds.setGlobalId(maybePid.get());
×
563
                } else {
564
                    // unparsable PID passed. Terminate.
565
                    return badRequest("Cannot parse the PID parameter '" + pidParam + "'. Make sure it is in valid form - see Dataverse Native API documentation.");
×
566
                }
567
            }
568

569
            boolean shouldRelease = StringUtil.isTrue(releaseParam);
×
570
            DataverseRequest request = createDataverseRequest(u);
×
571

572
            Dataset managedDs = null;
×
573
            if (nonEmpty(pidParam)) {
×
574
                managedDs = execCommand(new ImportDatasetCommand(ds, request));
×
575
            }
576
            else {
577
                managedDs = execCommand(new CreateNewDatasetCommand(ds, request));
×
578
            }
579

580
            JsonObjectBuilder responseBld = Json.createObjectBuilder()
×
581
                    .add("id", managedDs.getId())
×
582
                    .add("persistentId", managedDs.getGlobalId().toString());
×
583

584
            if (shouldRelease) {
×
585
                DatasetVersion latestVersion = ds.getLatestVersion();
×
586
                latestVersion.setVersionState(DatasetVersion.VersionState.RELEASED);
×
587
                latestVersion.setVersionNumber(1l);
×
588
                latestVersion.setMinorVersionNumber(0l);
×
589
                if (latestVersion.getCreateTime() != null) {
×
590
                    latestVersion.setCreateTime(new Date());
×
591
                }
592
                if (latestVersion.getLastUpdateTime() != null) {
×
593
                    latestVersion.setLastUpdateTime(new Date());
×
594
                }
595
                PublishDatasetResult res = execCommand(new PublishDatasetCommand(managedDs, request, false, shouldRelease));
×
596
                responseBld.add("releaseCompleted", res.isCompleted());
×
597
            }
598

599
            return created("/datasets/" + managedDs.getId(), responseBld);
×
600

601
        } catch (WrappedResponse ex) {
×
602
            return ex.getResponse();
×
603
        }
604
    }
605

606
    @POST
607
    @AuthRequired
608
    @Path("{identifier}/datasets/:startmigration")
609
    @Consumes("application/ld+json, application/json-ld")
610
    public Response recreateDataset(@Context ContainerRequestContext crc, String jsonLDBody, @PathParam("identifier") String parentIdtf) {
611
        try {
612
            User u = getRequestUser(crc);
×
613
            if (!u.isSuperuser()) {
×
614
                return error(Status.FORBIDDEN, "Not a superuser");
×
615
            }
616
            Dataverse owner = findDataverseOrDie(parentIdtf);
×
617
            
618
            Dataset ds = new Dataset();
×
619

620
            ds.setOwner(owner);
×
621
            ds = JSONLDUtil.updateDatasetMDFromJsonLD(ds, jsonLDBody, metadataBlockSvc, datasetFieldSvc, false, true, licenseSvc, datasetTypeSvc);
×
622
          //ToDo - verify PID is one Dataverse can manage (protocol/authority/shoulder match)
623
          if (!PidUtil.getPidProvider(ds.getGlobalId().getProviderId()).canManagePID()) {
×
624
              throw new BadRequestException(
×
625
                      "Cannot recreate a dataset that has a PID that doesn't match the server's settings");
626
          }
627
            if(!dvObjectSvc.isGlobalIdLocallyUnique(ds.getGlobalId())) {
×
628
                throw new BadRequestException("Cannot recreate a dataset whose PID is already in use");
×
629
            }
630
            
631
            //Throw BadRequestException if metadataLanguage isn't compatible with setting
632
            DataverseUtil.checkMetadataLangauge(ds, owner, settingsService.getBaseMetadataLanguageMap(null, true));
×
633

634

635
            if (ds.getVersions().isEmpty()) {
×
636
                return badRequest("Supplied json must contain a single dataset version.");
×
637
            }
638

639
            DatasetVersion version = ds.getVersions().get(0);
×
640
            if (!version.isPublished()) {
×
641
                throw new BadRequestException("Cannot recreate a dataset that hasn't been published.");
×
642
            }
643
            //While the datasetversion whose metadata we're importing has been published, we consider it in draft until the API caller adds files and then completes the migration
644
            version.setVersionState(DatasetVersion.VersionState.DRAFT);
×
645

646
            DataverseRequest request = createDataverseRequest(u);
×
647

648
            Dataset managedDs = execCommand(new ImportDatasetCommand(ds, request));
×
649
            JsonObjectBuilder responseBld = Json.createObjectBuilder()
×
650
                    .add("id", managedDs.getId())
×
651
                    .add("persistentId", managedDs.getGlobalId().toString());
×
652

653
            return created("/datasets/" + managedDs.getId(), responseBld);
×
654

655
        } catch (WrappedResponse ex) {
×
656
            return ex.getResponse();
×
657
        }
658
    }
659
    
660
    private Dataset parseDataset(String datasetJson) throws WrappedResponse {
661
        try {
662
            return jsonParser().parseDataset(JsonUtil.getJsonObject(datasetJson));
×
663
        } catch (JsonParsingException | JsonParseException jpe) {
×
664
            String message = jpe.getLocalizedMessage();
×
665
            logger.log(Level.SEVERE, "Error parsing dataset JSON. message: {0}", message);
×
666
            logger.log(Level.SEVERE, "Error parsing dataset json. Json: {0}", datasetJson);
×
667
            throw new WrappedResponse(error(Status.BAD_REQUEST, "Error parsing Json: " + jpe.getMessage()));
×
668
        }
669
    }
670

671
    @GET
672
    @AuthRequired
673
    @Path("{identifier}")
674
    public Response getDataverse(@Context ContainerRequestContext crc, @PathParam("identifier") String idtf, @QueryParam("returnOwners") boolean returnOwners) {
675
        return response(req -> ok(
×
676
            json(execCommand(new GetDataverseCommand(req, findDataverseOrDie(idtf))),
×
677
                settingsService.isTrueForKey(SettingsServiceBean.Key.ExcludeEmailFromExport, false),
×
678
                returnOwners
×
679
            )), getRequestUser(crc));
×
680
    }
681

682
    @DELETE
683
    @AuthRequired
684
    @Path("{identifier}")
685
    public Response deleteDataverse(@Context ContainerRequestContext crc, @PathParam("identifier") String idtf) {
686
        return response(req -> {
×
687
            execCommand(new DeleteDataverseCommand(req, findDataverseOrDie(idtf)));
×
688
            return ok("Dataverse " + idtf + " deleted");
×
689
        }, getRequestUser(crc));
×
690
    }
691

692
    /**
693
     * Endpoint to change attributes of a Dataverse collection.
694
     *
695
     * @apiNote Example curl command:
696
     *          <code>curl -X PUT -d "test" http://localhost:8080/api/dataverses/$ALIAS/attribute/alias</code>
697
     *          to change the alias of the collection named $ALIAS to "test".
698
     */
699
    @PUT
700
    @AuthRequired
701
    @Path("{identifier}/attribute/{attribute}")
702
    public Response updateAttribute(@Context ContainerRequestContext crc, @PathParam("identifier") String identifier,
703
                                    @PathParam("attribute") String attribute, @QueryParam("value") String value) {
704
        try {
705
            Dataverse dataverse = findDataverseOrDie(identifier);
×
706
            Object formattedValue = formatAttributeValue(attribute, value);
×
707
            dataverse = execCommand(new UpdateDataverseAttributeCommand(createDataverseRequest(getRequestUser(crc)), dataverse, attribute, formattedValue));
×
708
            return ok("Update successful", JsonPrinter.json(dataverse));
×
709
        } catch (WrappedResponse e) {
×
710
            return e.getResponse();
×
711
        }
712
    }
713

714
    private Object formatAttributeValue(String attribute, String value) throws WrappedResponse {
715
        if (attribute.equals("filePIDsEnabled")) {
×
716
            return parseBooleanOrDie(value);
×
717
        }
718
        return value;
×
719
    }
720

721
    @GET
722
    @AuthRequired
723
    @Path("{identifier}/inputLevels")
724
    public Response getInputLevels(@Context ContainerRequestContext crc, @PathParam("identifier") String identifier) {
725
        try {
726
            Dataverse dataverse = findDataverseOrDie(identifier);
×
727
            List<DataverseFieldTypeInputLevel> inputLevels = execCommand(new ListDataverseInputLevelsCommand(createDataverseRequest(getRequestUser(crc)), dataverse));
×
728
            return ok(jsonDataverseInputLevels(inputLevels));
×
729
        } catch (WrappedResponse e) {
×
730
            return e.getResponse();
×
731
        }
732
    }
733

734
    @PUT
735
    @AuthRequired
736
    @Path("{identifier}/inputLevels")
737
    public Response updateInputLevels(@Context ContainerRequestContext crc, @PathParam("identifier") String identifier, String jsonBody) {
738
        try {
739
            Dataverse dataverse = findDataverseOrDie(identifier);
×
740
            List<DataverseFieldTypeInputLevel> newInputLevels = parseInputLevels(Json.createReader(new StringReader(jsonBody)).readArray(), dataverse);
×
741
            execCommand(new UpdateDataverseInputLevelsCommand(dataverse, createDataverseRequest(getRequestUser(crc)), newInputLevels));
×
742
            return ok(BundleUtil.getStringFromBundle("dataverse.update.success"), JsonPrinter.json(dataverse));
×
743
        } catch (WrappedResponse e) {
×
744
            return e.getResponse();
×
745
        }
746
    }
747

748
    private List<DataverseFieldTypeInputLevel> parseInputLevels(JsonArray inputLevelsArray, Dataverse dataverse) throws WrappedResponse {
749
        List<DataverseFieldTypeInputLevel> newInputLevels = new ArrayList<>();
×
750
        for (JsonValue value : inputLevelsArray) {
×
751
            JsonObject inputLevel = (JsonObject) value;
×
752
            String datasetFieldTypeName = inputLevel.getString("datasetFieldTypeName");
×
753
            DatasetFieldType datasetFieldType = datasetFieldSvc.findByName(datasetFieldTypeName);
×
754

755
            if (datasetFieldType == null) {
×
756
                String errorMessage = MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.inputlevels.error.invalidfieldtypename"), datasetFieldTypeName);
×
757
                throw new WrappedResponse(badRequest(errorMessage));
×
758
            }
759

760
            boolean required = inputLevel.getBoolean("required");
×
761
            boolean include = inputLevel.getBoolean("include");
×
762

763
            if (required && !include) {
×
764
                String errorMessage = MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.inputlevels.error.cannotberequiredifnotincluded"), datasetFieldTypeName);
×
765
                throw new WrappedResponse(badRequest(errorMessage));
×
766
            }
767

768
            newInputLevels.add(new DataverseFieldTypeInputLevel(datasetFieldType, dataverse, required, include));
×
769
        }
×
770

771
        return newInputLevels;
×
772
    }
773

774
    private List<DatasetFieldType> parseFacets(JsonArray facetsArray) throws WrappedResponse {
775
        List<DatasetFieldType> facets = new LinkedList<>();
×
776
        for (JsonString facetId : facetsArray.getValuesAs(JsonString.class)) {
×
777
            DatasetFieldType dsfType = findDatasetFieldType(facetId.getString());
×
778
            if (dsfType == null) {
×
779
                throw new WrappedResponse(badRequest(MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.facets.error.fieldtypenotfound"), facetId)));
×
780
            } else if (!dsfType.isFacetable()) {
×
781
                throw new WrappedResponse(badRequest(MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.facets.error.fieldtypenotfacetable"), facetId)));
×
782
            }
783
            facets.add(dsfType);
×
784
        }
×
785
        return facets;
×
786
    }
787

788
    @DELETE
789
    @AuthRequired
790
    @Path("{linkingDataverseId}/deleteLink/{linkedDataverseId}")
791
    public Response deleteDataverseLinkingDataverse(@Context ContainerRequestContext crc, @PathParam("linkingDataverseId") String linkingDataverseId, @PathParam("linkedDataverseId") String linkedDataverseId) {
792
        boolean index = true;
×
793
        return response(req -> {
×
794
            execCommand(new DeleteDataverseLinkingDataverseCommand(req, findDataverseOrDie(linkingDataverseId), findDataverseLinkingDataverseOrDie(linkingDataverseId, linkedDataverseId), index));
×
795
            return ok("Link from Dataverse " + linkingDataverseId + " to linked Dataverse " + linkedDataverseId + " deleted");
×
796
        }, getRequestUser(crc));
×
797
    }
798

799
    @GET
800
    @AuthRequired
801
    @Path("{identifier}/metadatablocks")
802
    public Response listMetadataBlocks(@Context ContainerRequestContext crc,
803
                                       @PathParam("identifier") String dvIdtf,
804
                                       @QueryParam("onlyDisplayedOnCreate") boolean onlyDisplayedOnCreate,
805
                                       @QueryParam("returnDatasetFieldTypes") boolean returnDatasetFieldTypes,
806
                                       @QueryParam("datasetType") String datasetTypeIn) {
807
        try {
808
            Dataverse dataverse = findDataverseOrDie(dvIdtf);
×
NEW
809
            DatasetType datasetType = datasetTypeSvc.getByName(datasetTypeIn);
×
UNCOV
810
            final List<MetadataBlock> metadataBlocks = execCommand(
×
811
                    new ListMetadataBlocksCommand(
812
                            createDataverseRequest(getRequestUser(crc)),
×
813
                            dataverse,
814
                            onlyDisplayedOnCreate,
815
                            datasetType
816
                    )
817
            );
NEW
818
            return ok(json(metadataBlocks, returnDatasetFieldTypes, onlyDisplayedOnCreate, dataverse, datasetType));
×
819
        } catch (WrappedResponse we) {
×
820
            return we.getResponse();
×
821
        }
822
    }
823

824
    @POST
825
    @AuthRequired
826
    @Path("{identifier}/metadatablocks")
827
    @Produces(MediaType.APPLICATION_JSON)
828
    public Response setMetadataBlocks(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf, String blockIds) {
829

830
        List<MetadataBlock> blocks = new LinkedList<>();
×
831
        try {
832
            for (JsonValue blockId : Util.asJsonArray(blockIds).getValuesAs(JsonValue.class)) {
×
833
                MetadataBlock blk = (blockId.getValueType() == ValueType.NUMBER)
×
834
                        ? findMetadataBlock(((JsonNumber) blockId).longValue())
×
835
                        : findMetadataBlock(((JsonString) blockId).getString());
×
836
                if (blk == null) {
×
837
                    return error(Response.Status.BAD_REQUEST, "Can't find metadata block '" + blockId + "'");
×
838
                }
839
                blocks.add(blk);
×
840
            }
×
841
        } catch (Exception e) {
×
842
            return error(Response.Status.BAD_REQUEST, e.getMessage());
×
843
        }
×
844

845
        try {
846
            execCommand(new UpdateDataverseMetadataBlocksCommand.SetBlocks(createDataverseRequest(getRequestUser(crc)), findDataverseOrDie(dvIdtf), blocks));
×
847
            return ok("Metadata blocks of dataverse " + dvIdtf + " updated.");
×
848

849
        } catch (WrappedResponse ex) {
×
850
            return ex.getResponse();
×
851
        }
852
    }
853

854
    @GET
855
    @AuthRequired
856
    @Path("{identifier}/metadatablocks/:isRoot")
857
    public Response getMetadataRoot_legacy(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf) {
858
        return getMetadataRoot(crc, dvIdtf);
×
859
    }
860

861
    @GET
862
    @AuthRequired
863
    @Path("{identifier}/metadatablocks/isRoot")
864
    @Produces(MediaType.APPLICATION_JSON)
865
    public Response getMetadataRoot(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf) {
866
        return response(req -> {
×
867
            final Dataverse dataverse = findDataverseOrDie(dvIdtf);
×
868
            if (permissionSvc.request(req)
×
869
                    .on(dataverse)
×
870
                    .has(Permission.EditDataverse)) {
×
871
                return ok(dataverse.isMetadataBlockRoot());
×
872
            } else {
873
                return error(Status.FORBIDDEN, "Not authorized");
×
874
            }
875
        }, getRequestUser(crc));
×
876
    }
877

878
    @POST
879
    @AuthRequired
880
    @Path("{identifier}/metadatablocks/:isRoot")
881
    @Produces(MediaType.APPLICATION_JSON)
882
    @Consumes(MediaType.WILDCARD)
883
    public Response setMetadataRoot_legacy(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf, String body) {
884
        return setMetadataRoot(crc, dvIdtf, body);
×
885
    }
886

887
    @PUT
888
    @AuthRequired
889
    @Path("{identifier}/metadatablocks/isRoot")
890
    @Produces(MediaType.APPLICATION_JSON)
891
    @Consumes(MediaType.WILDCARD)
892
    public Response setMetadataRoot(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf, String body) {
893
        return response(req -> {
×
894
            final boolean root = parseBooleanOrDie(body);
×
895
            final Dataverse dataverse = findDataverseOrDie(dvIdtf);
×
896
            execCommand(new UpdateDataverseMetadataBlocksCommand.SetRoot(req, dataverse, root));
×
897
            return ok("Dataverse " + dataverse.getName() + " is now a metadata  " + (root ? "" : "non-") + "root");
×
898
        }, getRequestUser(crc));
×
899
    }
900

901
    @GET
902
    @AuthRequired
903
    @Path("{identifier}/facets/")
904
    /**
905
     * return list of facets for the dataverse with alias `dvIdtf`
906
     */
907
    public Response listFacets(@Context ContainerRequestContext crc,
908
                               @PathParam("identifier") String dvIdtf,
909
                               @QueryParam("returnDetails") boolean returnDetails) {
910
        try {
911
            User user = getRequestUser(crc);
×
912
            DataverseRequest request = createDataverseRequest(user);
×
913
            Dataverse dataverse = findDataverseOrDie(dvIdtf);
×
914
            List<DataverseFacet> dataverseFacets = execCommand(new ListFacetsCommand(request, dataverse));
×
915

916
            if (returnDetails) {
×
917
                return ok(jsonDataverseFacets(dataverseFacets));
×
918
            } else {
919
                JsonArrayBuilder facetsBuilder = Json.createArrayBuilder();
×
920
                for (DataverseFacet facet : dataverseFacets) {
×
921
                    facetsBuilder.add(facet.getDatasetFieldType().getName());
×
922
                }
×
923
                return ok(facetsBuilder);
×
924
            }
925
        } catch (WrappedResponse e) {
×
926
            return e.getResponse();
×
927
        }
928
    }
929

930
    @GET
931
    @AuthRequired
932
    @Path("{identifier}/featured")
933
    /*
934
    Allows user to get the collections that are featured by a given collection
935
    probably more for SPA than end user
936
    */
937
    public Response getFeaturedDataverses(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf,  String dvAliases) {
938

939
        try {
940
            User u = getRequestUser(crc);
×
941
            DataverseRequest r = createDataverseRequest(u);
×
942
            Dataverse dataverse = findDataverseOrDie(dvIdtf);
×
943
            JsonArrayBuilder fs = Json.createArrayBuilder();
×
944
            for (Dataverse f : execCommand(new ListFeaturedCollectionsCommand(r, dataverse))) {
×
945
                fs.add(f.getAlias());
×
946
            }
×
947
            return ok(fs);
×
948
        } catch (WrappedResponse e) {
×
949
            return e.getResponse();
×
950
        }
951
    }
952

953

954
    @POST
955
    @AuthRequired
956
    @Path("{identifier}/featured")
957
    /**
958
     * Allows user to set featured dataverses - must have edit dataverse permission
959
     *
960
     */
961
    public Response setFeaturedDataverses(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf,  String dvAliases) {
962
        List<Dataverse> dvsFromInput = new LinkedList<>();
×
963

964

965
        try {
966

967
            for (JsonString dvAlias : Util.asJsonArray(dvAliases).getValuesAs(JsonString.class)) {
×
968
                Dataverse dvToBeFeatured = dataverseService.findByAlias(dvAlias.getString());
×
969
                if (dvToBeFeatured == null) {
×
970
                    return error(Response.Status.BAD_REQUEST, "Can't find dataverse collection with alias '" + dvAlias + "'");
×
971
                }
972
                dvsFromInput.add(dvToBeFeatured);
×
973
            }
×
974

975
            if (dvsFromInput.isEmpty()) {
×
976
                return error(Response.Status.BAD_REQUEST, "Please provide a valid Json array of dataverse collection aliases to be featured.");
×
977
            }
978

979
            Dataverse dataverse = findDataverseOrDie(dvIdtf);
×
980
            List<Dataverse> featuredSource = new ArrayList<>();
×
981
            List<Dataverse> featuredTarget = new ArrayList<>();
×
982
            featuredSource.addAll(dataverseService.findAllPublishedByOwnerId(dataverse.getId()));
×
983
            featuredSource.addAll(linkingService.findLinkedDataverses(dataverse.getId()));
×
984
            List<DataverseFeaturedDataverse> featuredList = featuredDataverseService.findByDataverseId(dataverse.getId());
×
985

986
            if (featuredSource.isEmpty()) {
×
987
                return error(Response.Status.BAD_REQUEST, "There are no collections avaialble to be featured in Dataverse collection '" + dataverse.getDisplayName() + "'.");
×
988
            }
989

990
            for (DataverseFeaturedDataverse dfd : featuredList) {
×
991
                Dataverse fd = dfd.getFeaturedDataverse();
×
992
                featuredTarget.add(fd);
×
993
                featuredSource.remove(fd);
×
994
            }
×
995

996
            for (Dataverse test : dvsFromInput) {
×
997
                if (featuredTarget.contains(test)) {
×
998
                    return error(Response.Status.BAD_REQUEST, "Dataverse collection '" + test.getDisplayName() + "' is already featured in Dataverse collection '" + dataverse.getDisplayName() + "'.");
×
999
                }
1000

1001
                if (featuredSource.contains(test)) {
×
1002
                    featuredTarget.add(test);
×
1003
                } else {
1004
                    return error(Response.Status.BAD_REQUEST, "Dataverse collection '" + test.getDisplayName() + "' may not be featured in Dataverse collection '" + dataverse.getDisplayName() + "'.");
×
1005
                }
1006

1007
            }
×
1008
            // by passing null for Facets and DataverseFieldTypeInputLevel, those are not changed
1009
            execCommand(new UpdateDataverseCommand(dataverse, null, featuredTarget, createDataverseRequest(getRequestUser(crc)), null));
×
1010
            return ok("Featured Dataverses of dataverse " + dvIdtf + " updated.");
×
1011

1012
        } catch (WrappedResponse ex) {
×
1013
            return ex.getResponse();
×
1014
        } catch (JsonParsingException jpe){
×
1015
            return error(Response.Status.BAD_REQUEST, "Please provide a valid Json array of dataverse collection aliases to be featured.");
×
1016
        }
1017

1018
    }
1019

1020
    @DELETE
1021
    @AuthRequired
1022
    @Path("{identifier}/featured")
1023
    public Response deleteFeaturedCollections(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf) throws WrappedResponse {
1024
        try {
1025
            Dataverse dataverse = findDataverseOrDie(dvIdtf);
×
1026
            List<Dataverse> featuredTarget = new ArrayList<>();
×
1027
            execCommand(new UpdateDataverseCommand(dataverse, null, featuredTarget, createDataverseRequest(getRequestUser(crc)), null));
×
1028
            return ok(BundleUtil.getStringFromBundle("dataverses.api.delete.featured.collections.successful"));
×
1029
        } catch (WrappedResponse ex) {
×
1030
            return ex.getResponse();
×
1031
        }
1032
    }
1033

1034
    @POST
1035
    @AuthRequired
1036
    @Path("{identifier}/facets")
1037
    @Produces(MediaType.APPLICATION_JSON)
1038
    /**
1039
     * (not publicly documented) API endpoint for assigning facets to a
1040
     * dataverse. `curl -X POST -H "X-Dataverse-key: $ADMIN_KEY"
1041
     * http://localhost:8088/api/dataverses/$dv/facets --upload-file foo.json`;
1042
     * where foo.json contains a list of datasetField names, works as expected
1043
     * (judging by the UI). This triggers a 500 when '-d @foo.json' is used.
1044
     */
1045
    public Response setFacets(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf, String facetIds) {
1046
        JsonArray jsonArray = Util.asJsonArray(facetIds);
×
1047
        List<DatasetFieldType> facets;
1048
        try {
1049
            facets = parseFacets(jsonArray);
×
1050
        } catch (WrappedResponse e) {
×
1051
            return e.getResponse();
×
1052
        }
×
1053

1054
        try {
1055
            Dataverse dataverse = findDataverseOrDie(dvIdtf);
×
1056
            // by passing null for Featured Dataverses and DataverseFieldTypeInputLevel, those are not changed
1057
            execCommand(new UpdateDataverseCommand(dataverse, facets, null, createDataverseRequest(getRequestUser(crc)), null));
×
1058
            return ok("Facets of dataverse " + dvIdtf + " updated.");
×
1059

1060
        } catch (WrappedResponse ex) {
×
1061
            return ex.getResponse();
×
1062
        }
1063
    }
1064

1065
    @GET
1066
    @AuthRequired
1067
    @Path("{identifier}/metadatablockfacets")
1068
    @Produces(MediaType.APPLICATION_JSON)
1069
    public Response listMetadataBlockFacets(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf) {
1070
        try {
1071
            User u = getRequestUser(crc);
1✔
1072
            DataverseRequest request = createDataverseRequest(u);
1✔
1073
            Dataverse dataverse = findDataverseOrDie(dvIdtf);
1✔
1074
            List<DataverseMetadataBlockFacet> metadataBlockFacets = Optional.ofNullable(execCommand(new ListMetadataBlockFacetsCommand(request, dataverse))).orElse(Collections.emptyList());
1✔
1075
            List<DataverseMetadataBlockFacetDTO.MetadataBlockDTO> metadataBlocksDTOs = metadataBlockFacets.stream()
1✔
1076
                    .map(item -> new DataverseMetadataBlockFacetDTO.MetadataBlockDTO(item.getMetadataBlock().getName(), item.getMetadataBlock().getLocaleDisplayFacet()))
1✔
1077
                    .collect(Collectors.toList());
1✔
1078
            DataverseMetadataBlockFacetDTO response = new DataverseMetadataBlockFacetDTO(dataverse.getId(), dataverse.getAlias(), dataverse.isMetadataBlockFacetRoot(), metadataBlocksDTOs);
1✔
1079
            return Response.ok(response).build();
1✔
1080
        } catch (WrappedResponse e) {
1✔
1081
            return e.getResponse();
1✔
1082
        }
1083
    }
1084

1085
    @POST
1086
    @AuthRequired
1087
    @Path("{identifier}/metadatablockfacets")
1088
    @Consumes(MediaType.APPLICATION_JSON)
1089
    @Produces(MediaType.APPLICATION_JSON)
1090
    public Response setMetadataBlockFacets(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf, List<String> metadataBlockNames) {
1091
        try {
1092
            Dataverse dataverse = findDataverseOrDie(dvIdtf);
1✔
1093

1094
            if(!dataverse.isMetadataBlockFacetRoot()) {
1✔
1095
                return badRequest(String.format("Dataverse: %s must have metadata block facet root set to true", dvIdtf));
1✔
1096
            }
1097

1098
            List<DataverseMetadataBlockFacet> metadataBlockFacets = new LinkedList<>();
1✔
1099
            for(String metadataBlockName: metadataBlockNames) {
1✔
1100
                MetadataBlock metadataBlock = findMetadataBlock(metadataBlockName);
1✔
1101
                if (metadataBlock == null) {
1✔
1102
                    return badRequest(String.format("Invalid metadata block name: %s", metadataBlockName));
1✔
1103
                }
1104

1105
                DataverseMetadataBlockFacet metadataBlockFacet = new DataverseMetadataBlockFacet();
1✔
1106
                metadataBlockFacet.setDataverse(dataverse);
1✔
1107
                metadataBlockFacet.setMetadataBlock(metadataBlock);
1✔
1108
                metadataBlockFacets.add(metadataBlockFacet);
1✔
1109
            }
1✔
1110

1111
            execCommand(new UpdateMetadataBlockFacetsCommand(createDataverseRequest(getRequestUser(crc)), dataverse, metadataBlockFacets));
1✔
1112
            return ok(String.format("Metadata block facets updated. DataverseId: %s blocks: %s", dvIdtf, metadataBlockNames));
1✔
1113

1114
        } catch (WrappedResponse ex) {
1✔
1115
            return ex.getResponse();
1✔
1116
        }
1117
    }
1118

1119
    @POST
1120
    @AuthRequired
1121
    @Path("{identifier}/metadatablockfacets/isRoot")
1122
    @Consumes(MediaType.APPLICATION_JSON)
1123
    @Produces(MediaType.APPLICATION_JSON)
1124
    public Response updateMetadataBlockFacetsRoot(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf, String body) {
1125
        try {
1126
            final boolean blockFacetsRoot = parseBooleanOrDie(body);
1✔
1127
            Dataverse dataverse = findDataverseOrDie(dvIdtf);
1✔
1128
            if(dataverse.isMetadataBlockFacetRoot() == blockFacetsRoot) {
1✔
1129
                return ok(String.format("No update needed, dataverse already consistent with new value. DataverseId: %s blockFacetsRoot: %s", dvIdtf, blockFacetsRoot));
1✔
1130
            }
1131

1132
            execCommand(new UpdateMetadataBlockFacetRootCommand(createDataverseRequest(getRequestUser(crc)), dataverse, blockFacetsRoot));
1✔
1133
            return ok(String.format("Metadata block facets root updated. DataverseId: %s blockFacetsRoot: %s", dvIdtf, blockFacetsRoot));
1✔
1134

1135
        } catch (WrappedResponse ex) {
1✔
1136
            return ex.getResponse();
1✔
1137
        }
1138
    }
1139

1140
    // FIXME: This listContent method is way too optimistic, always returning "ok" and never "error".
1141
    // TODO: Investigate why there was a change in the timeframe of when pull request #4350 was merged
1142
    // (2438-4295-dois-for-files branch) such that a contributor API token no longer allows this method
1143
    // to be called without a PermissionException being thrown.
1144
    @GET
1145
    @AuthRequired
1146
    @Path("{identifier}/contents")
1147
    public Response listContent(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf) throws WrappedResponse {
1148

1149
        DvObject.Visitor<JsonObjectBuilder> ser = new DvObject.Visitor<JsonObjectBuilder>() {
×
1150
            @Override
1151
            public JsonObjectBuilder visit(Dataverse dv) {
1152
                return Json.createObjectBuilder().add("type", "dataverse")
×
1153
                        .add("id", dv.getId())
×
1154
                        .add("title", dv.getName());
×
1155
            }
1156

1157
            @Override
1158
            public JsonObjectBuilder visit(Dataset ds) {
1159
                return json(ds).add("type", "dataset");
×
1160
            }
1161

1162
            @Override
1163
            public JsonObjectBuilder visit(DataFile df) {
1164
                throw new UnsupportedOperationException("Files don't live directly in Dataverses");
×
1165
            }
1166
        };
1167

1168
        return response(req -> ok(
×
1169
                execCommand(new ListDataverseContentCommand(req, findDataverseOrDie(dvIdtf)))
×
1170
                        .stream()
×
1171
                        .map(dvo -> (JsonObjectBuilder) dvo.accept(ser))
×
1172
                        .collect(toJsonArray())
×
1173
        ), getRequestUser(crc));
×
1174
    }
1175

1176
    @GET
1177
    @AuthRequired
1178
    @Path("{identifier}/storagesize")
1179
    public Response getStorageSize(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf, @QueryParam("includeCached") boolean includeCached) throws WrappedResponse {
1180
                
1181
        return response(req -> ok(MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.datasize"),
×
1182
                execCommand(new GetDataverseStorageSizeCommand(req, findDataverseOrDie(dvIdtf), includeCached)))), getRequestUser(crc));
×
1183
    }
1184
    
1185
    @GET
1186
    @AuthRequired
1187
    @Path("{identifier}/storage/quota")
1188
    public Response getCollectionQuota(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf) throws WrappedResponse {
1189
        try {
1190
            Long bytesAllocated = execCommand(new GetCollectionQuotaCommand(createDataverseRequest(getRequestUser(crc)), findDataverseOrDie(dvIdtf)));
×
1191
            if (bytesAllocated != null) {
×
1192
                return ok(MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.storage.quota.allocation"),bytesAllocated));
×
1193
            }
1194
            return ok(BundleUtil.getStringFromBundle("dataverse.storage.quota.notdefined"));
×
1195
        } catch (WrappedResponse ex) {
×
1196
            return ex.getResponse();
×
1197
        }
1198
    }
1199
    
1200
    @POST
1201
    @AuthRequired
1202
    @Path("{identifier}/storage/quota/{bytesAllocated}")
1203
    public Response setCollectionQuota(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf, @PathParam("bytesAllocated") Long bytesAllocated) throws WrappedResponse {
1204
        try {
1205
            execCommand(new SetCollectionQuotaCommand(createDataverseRequest(getRequestUser(crc)), findDataverseOrDie(dvIdtf), bytesAllocated));
×
1206
            return ok(BundleUtil.getStringFromBundle("dataverse.storage.quota.updated"));
×
1207
        } catch (WrappedResponse ex) {
×
1208
            return ex.getResponse();
×
1209
        }
1210
    }
1211
    
1212
    @DELETE
1213
    @AuthRequired
1214
    @Path("{identifier}/storage/quota")
1215
    public Response deleteCollectionQuota(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf) throws WrappedResponse {
1216
        try {
1217
            execCommand(new DeleteCollectionQuotaCommand(createDataverseRequest(getRequestUser(crc)), findDataverseOrDie(dvIdtf)));
×
1218
            return ok(BundleUtil.getStringFromBundle("dataverse.storage.quota.deleted"));
×
1219
        } catch (WrappedResponse ex) {
×
1220
            return ex.getResponse();
×
1221
        }
1222
    }
1223
    
1224
    /**
1225
     *
1226
     * @param crc
1227
     * @param identifier
1228
     * @return
1229
     * @throws edu.harvard.iq.dataverse.api.AbstractApiBean.WrappedResponse 
1230
     * @todo: add an optional parameter that would force the recorded storage use
1231
     * to be recalculated (or should that be a POST version of this API?)
1232
     */
1233
    @GET
1234
    @AuthRequired
1235
    @Path("{identifier}/storage/use")
1236
    public Response getCollectionStorageUse(@Context ContainerRequestContext crc, @PathParam("identifier") String identifier) throws WrappedResponse {
1237
        return response(req -> ok(MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.storage.use"),
×
1238
                execCommand(new GetCollectionStorageUseCommand(req, findDataverseOrDie(identifier))))), getRequestUser(crc));
×
1239
    }
1240

1241
    @GET
1242
    @AuthRequired
1243
    @Path("{identifier}/roles")
1244
    public Response listRoles(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf) {
1245
        return response(req -> ok(
×
1246
                execCommand(new ListRolesCommand(req, findDataverseOrDie(dvIdtf)))
×
1247
                        .stream().map(r -> json(r))
×
1248
                        .collect(toJsonArray())
×
1249
        ), getRequestUser(crc));
×
1250
    }
1251

1252
    @POST
1253
    @AuthRequired
1254
    @Path("{identifier}/roles")
1255
    public Response createRole(@Context ContainerRequestContext crc, RoleDTO roleDto, @PathParam("identifier") String dvIdtf) {
1256
        return response(req -> ok(json(execCommand(new CreateRoleCommand(roleDto.asRole(), req, findDataverseOrDie(dvIdtf))))), getRequestUser(crc));
×
1257
    }
1258

1259
    @GET
1260
    @AuthRequired
1261
    @Path("{identifier}/assignments")
1262
    public Response listAssignments(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf) {
1263
        return response(req -> ok(
×
1264
                execCommand(new ListRoleAssignments(req, findDataverseOrDie(dvIdtf)))
×
1265
                        .stream()
×
1266
                        .map(a -> json(a))
×
1267
                        .collect(toJsonArray())
×
1268
        ), getRequestUser(crc));
×
1269
    }
1270

1271
    /**
1272
     * This code for setting a dataverse logo via API was started when initially
1273
     * investigating https://github.com/IQSS/dataverse/issues/3559 but it isn't
1274
     * finished so it's commented out. See also * "No functionality should be
1275
     * GUI-only. Make all functionality reachable via the API" at
1276
     * https://github.com/IQSS/dataverse/issues/3440
1277
     */
1278
//    File tempDir;
1279
//
1280
//    TODO: Code duplicate in ThemeWidgetFragment. Maybe extract, make static and put some place else?
1281
//          Important: at least use JvmSettings.DOCROOT_DIRECTORY and not the hardcoded location!
1282
//    private void createTempDir(Dataverse editDv) {
1283
//        try {
1284
//            File tempRoot = java.nio.file.Files.createDirectories(Paths.get("../docroot/logos/temp")).toFile();
1285
//            tempDir = java.nio.file.Files.createTempDirectory(tempRoot.toPath(), editDv.getId().toString()).toFile();
1286
//        } catch (IOException e) {
1287
//            throw new RuntimeException("Error creating temp directory", e); // improve error handling
1288
//        }
1289
//    }
1290
//
1291
//    private DataverseTheme initDataverseTheme(Dataverse editDv) {
1292
//        DataverseTheme dvt = new DataverseTheme();
1293
//        dvt.setLinkColor(DEFAULT_LINK_COLOR);
1294
//        dvt.setLogoBackgroundColor(DEFAULT_LOGO_BACKGROUND_COLOR);
1295
//        dvt.setBackgroundColor(DEFAULT_BACKGROUND_COLOR);
1296
//        dvt.setTextColor(DEFAULT_TEXT_COLOR);
1297
//        dvt.setDataverse(editDv);
1298
//        return dvt;
1299
//    }
1300
//
1301
//    @PUT
1302
//    @Path("{identifier}/logo")
1303
//    @Consumes(MediaType.MULTIPART_FORM_DATA)
1304
//    public Response setDataverseLogo(@PathParam("identifier") String dvIdtf,
1305
//            @FormDataParam("file") InputStream fileInputStream,
1306
//            @FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
1307
//            @QueryParam("key") String apiKey) {
1308
//        boolean disabled = true;
1309
//        if (disabled) {
1310
//            return error(Status.FORBIDDEN, "Setting the dataverse logo via API needs more work.");
1311
//        }
1312
//        try {
1313
//            final DataverseRequest req = createDataverseRequest(findUserOrDie());
1314
//            final Dataverse editDv = findDataverseOrDie(dvIdtf);
1315
//
1316
//            logger.finer("entering fileUpload");
1317
//            if (tempDir == null) {
1318
//                createTempDir(editDv);
1319
//                logger.finer("created tempDir");
1320
//            }
1321
//            File uploadedFile;
1322
//            try {
1323
//                String fileName = contentDispositionHeader.getFileName();
1324
//
1325
//                uploadedFile = new File(tempDir, fileName);
1326
//                if (!uploadedFile.exists()) {
1327
//                    uploadedFile.createNewFile();
1328
//                }
1329
//                logger.finer("created file");
1330
//                File file = null;
1331
//                file = FileUtil.inputStreamToFile(fileInputStream);
1332
//                if (file.length() > systemConfig.getUploadLogoSizeLimit()) {
1333
//                    return error(Response.Status.BAD_REQUEST, "File is larger than maximum size: " + systemConfig.getUploadLogoSizeLimit() + ".");
1334
//                }
1335
//                java.nio.file.Files.copy(fileInputStream, uploadedFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
1336
//                logger.finer("copied inputstream to file");
1337
//                editDv.setDataverseTheme(initDataverseTheme(editDv));
1338
//                editDv.getDataverseTheme().setLogo(fileName);
1339
//
1340
//            } catch (IOException e) {
1341
//                logger.finer("caught IOException");
1342
//                logger.throwing("ThemeWidgetFragment", "handleImageFileUpload", e);
1343
//                throw new RuntimeException("Error uploading logo file", e); // improve error handling
1344
//            }
1345
//            // If needed, set the default values for the logo
1346
//            if (editDv.getDataverseTheme().getLogoFormat() == null) {
1347
//                editDv.getDataverseTheme().setLogoFormat(DataverseTheme.ImageFormat.SQUARE);
1348
//            }
1349
//            logger.finer("end handelImageFileUpload");
1350
//            UpdateDataverseThemeCommand cmd = new UpdateDataverseThemeCommand(editDv, uploadedFile, req);
1351
//            Dataverse saved = execCommand(cmd);
1352
//
1353
//            /**
1354
//             * @todo delete the temp file:
1355
//             * docroot/logos/temp/1148114212463761832421/cc0.png
1356
//             */
1357
//            return ok("logo uploaded: " + saved.getDataverseTheme().getLogo());
1358
//        } catch (WrappedResponse ex) {
1359
//            return error(Status.BAD_REQUEST, "problem uploading logo: " + ex);
1360
//        }
1361
//    }
1362
    @POST
1363
    @AuthRequired
1364
    @Path("{identifier}/assignments")
1365
    public Response createAssignment(@Context ContainerRequestContext crc, RoleAssignmentDTO ra, @PathParam("identifier") String dvIdtf, @QueryParam("key") String apiKey) {
1366

1367
        try {
1368
            final DataverseRequest req = createDataverseRequest(getRequestUser(crc));
×
1369
            final Dataverse dataverse = findDataverseOrDie(dvIdtf);
×
1370

1371
            RoleAssignee assignee = findAssignee(ra.getAssignee());
×
1372
            if (assignee == null) {
×
1373
                return error(Status.BAD_REQUEST, "Assignee not found");
×
1374
            }
1375

1376
            DataverseRole theRole;
1377
            Dataverse dv = dataverse;
×
1378
            theRole = null;
×
1379
            while ((theRole == null) && (dv != null)) {
×
1380
                for (DataverseRole aRole : rolesSvc.availableRoles(dv.getId())) {
×
1381
                    if (aRole.getAlias().equals(ra.getRole())) {
×
1382
                        theRole = aRole;
×
1383
                        break;
×
1384
                    }
1385
                }
×
1386
                dv = dv.getOwner();
×
1387
            }
1388
            if (theRole == null) {
×
1389
                return error(Status.BAD_REQUEST, "Can't find role named '" + ra.getRole() + "' in dataverse " + dataverse);
×
1390
            }
1391
            String privateUrlToken = null;
×
1392

1393
            return ok(json(execCommand(new AssignRoleCommand(assignee, theRole, dataverse, req, privateUrlToken))));
×
1394

1395
        } catch (WrappedResponse ex) {
×
1396
            logger.log(Level.WARNING, "Can''t create assignment: {0}", ex.getMessage());
×
1397
            return ex.getResponse();
×
1398
        }
1399
    }
1400

1401
    @DELETE
1402
    @AuthRequired
1403
    @Path("{identifier}/assignments/{id}")
1404
    public Response deleteAssignment(@Context ContainerRequestContext crc, @PathParam("id") long assignmentId, @PathParam("identifier") String dvIdtf) {
1405
        RoleAssignment ra = em.find(RoleAssignment.class, assignmentId);
×
1406
        if (ra != null) {
×
1407
            try {
1408
                findDataverseOrDie(dvIdtf);
×
1409
                execCommand(new RevokeRoleCommand(ra, createDataverseRequest(getRequestUser(crc))));
×
1410
                return ok("Role " + ra.getRole().getName()
×
1411
                        + " revoked for assignee " + ra.getAssigneeIdentifier()
×
1412
                        + " in " + ra.getDefinitionPoint().accept(DvObject.NamePrinter));
×
1413
            } catch (WrappedResponse ex) {
×
1414
                return ex.getResponse();
×
1415
            }
1416
        } else {
1417
            return error(Status.NOT_FOUND, "Role assignment " + assignmentId + " not found");
×
1418
        }
1419
    }
1420

1421
    @POST
1422
    @AuthRequired
1423
    @Path("{identifier}/actions/:publish")
1424
    public Response publishDataverse(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf) {
1425
        try {
1426
            Dataverse dv = findDataverseOrDie(dvIdtf);
×
1427
            return ok(json(execCommand(new PublishDataverseCommand(createDataverseRequest(getRequestAuthenticatedUserOrDie(crc)), dv))));
×
1428

1429
        } catch (WrappedResponse wr) {
×
1430
            return wr.getResponse();
×
1431
        }
1432
    }
1433

1434
    @POST
1435
    @AuthRequired
1436
    @Path("{identifier}/groups/")
1437
    public Response createExplicitGroup(@Context ContainerRequestContext crc, ExplicitGroupDTO dto, @PathParam("identifier") String dvIdtf) {
1438
        return response(req -> {
×
1439
            ExplicitGroupProvider prv = explicitGroupSvc.getProvider();
×
1440
            ExplicitGroup newGroup = dto.apply(prv.makeGroup());
×
1441

1442
            newGroup = execCommand(new CreateExplicitGroupCommand(req, findDataverseOrDie(dvIdtf), newGroup));
×
1443

1444
            String groupUri = String.format("%s/groups/%s", dvIdtf, newGroup.getGroupAliasInOwner());
×
1445
            return created(groupUri, json(newGroup));
×
1446
        }, getRequestUser(crc));
×
1447
    }
1448

1449
    @GET
1450
    @AuthRequired
1451
    @Path("{identifier}/groups/")
1452
    public Response listGroups(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf, @QueryParam("key") String apiKey) {
1453
        return response(req -> ok(
×
1454
                execCommand(new ListExplicitGroupsCommand(req, findDataverseOrDie(dvIdtf)))
×
1455
                        .stream().map(eg -> json(eg))
×
1456
                        .collect(toJsonArray())
×
1457
        ), getRequestUser(crc));
×
1458
    }
1459

1460
    @GET
1461
    @AuthRequired
1462
    @Path("{identifier}/groups/{aliasInOwner}")
1463
    public Response getGroupByOwnerAndAliasInOwner(@Context ContainerRequestContext crc,
1464
                                                   @PathParam("identifier") String dvIdtf,
1465
                                                   @PathParam("aliasInOwner") String grpAliasInOwner) {
1466
        return response(req -> ok(json(findExplicitGroupOrDie(findDataverseOrDie(dvIdtf),
×
1467
                req,
1468
                grpAliasInOwner))), getRequestUser(crc));
×
1469
    }
1470
    
1471
    @GET
1472
    @AuthRequired
1473
    @Path("{identifier}/guestbookResponses/")
1474
    public Response getGuestbookResponsesByDataverse(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf,
1475
            @QueryParam("guestbookId") Long gbId, @Context HttpServletResponse response) {
1476

1477
        Dataverse dv;
1478
        try {
1479
            dv = findDataverseOrDie(dvIdtf);
×
1480
            User u = getRequestUser(crc);
×
1481
            DataverseRequest req = createDataverseRequest(u);
×
1482
            if (permissionSvc.request(req)
×
1483
                    .on(dv)
×
1484
                    .has(Permission.EditDataverse)) {
×
1485
            } else {
1486
                return error(Status.FORBIDDEN, "Not authorized");
×
1487
            }
1488

1489
        } catch (WrappedResponse wr) {
×
1490
            return wr.getResponse();
×
1491
        }
×
1492

1493
        StreamingOutput stream = new StreamingOutput() {
×
1494

1495
            @Override
1496
            public void write(OutputStream os) throws IOException,
1497
                    WebApplicationException {
1498

1499
                Map<Integer, Object> customQandAs = guestbookResponseService.mapCustomQuestionAnswersAsStrings(dv.getId(), gbId);
×
1500
                Map<Integer, String> datasetTitles = guestbookResponseService.mapDatasetTitles(dv.getId());
×
1501

1502
                List<Object[]> guestbookResults = guestbookResponseService.getGuestbookResults(dv.getId(), gbId);
×
1503
                os.write("Guestbook, Dataset, Dataset PID, Date, Type, File Name, File Id, File PID, User Name, Email, Institution, Position, Custom Questions\n".getBytes());
×
1504
                for (Object[] result : guestbookResults) {
×
1505
                    StringBuilder sb = guestbookResponseService.convertGuestbookResponsesToCSV(customQandAs, datasetTitles, result);
×
1506
                    os.write(sb.toString().getBytes());
×
1507
                }
×
1508
            }
×
1509
        };
1510
        return Response.ok(stream).build();
×
1511
    }
1512
    
1513
    @PUT
1514
    @AuthRequired
1515
    @Path("{identifier}/groups/{aliasInOwner}")
1516
    public Response updateGroup(@Context ContainerRequestContext crc, ExplicitGroupDTO groupDto,
1517
            @PathParam("identifier") String dvIdtf,
1518
            @PathParam("aliasInOwner") String grpAliasInOwner) {
1519
        return response(req -> ok(json(execCommand(
×
1520
                new UpdateExplicitGroupCommand(req,
1521
                        groupDto.apply(findExplicitGroupOrDie(findDataverseOrDie(dvIdtf), req, grpAliasInOwner)))))), getRequestUser(crc));
×
1522
    }
1523
    
1524
    @PUT
1525
    @AuthRequired
1526
    @Path("{identifier}/defaultContributorRole/{roleAlias}")
1527
    public Response updateDefaultContributorRole(
1528
            @Context ContainerRequestContext crc,
1529
            @PathParam("identifier") String dvIdtf,
1530
            @PathParam("roleAlias") String roleAlias) {
1531

1532
        DataverseRole defaultRole;
1533
        
1534
        if (roleAlias.equals(DataverseRole.NONE)) {
×
1535
            defaultRole = null;
×
1536
        } else {
1537
            try {
1538
                Dataverse dv = findDataverseOrDie(dvIdtf);
×
1539
                defaultRole = rolesSvc.findCustomRoleByAliasAndOwner(roleAlias, dv.getId());
×
1540
            } catch (Exception nre) {
×
1541
                List<String> args = Arrays.asList(roleAlias);
×
1542
                String retStringError = BundleUtil.getStringFromBundle("dataverses.api.update.default.contributor.role.failure.role.not.found", args);
×
1543
                return error(Status.NOT_FOUND, retStringError);
×
1544
            }
×
1545

1546
            if (!defaultRole.doesDvObjectClassHavePermissionForObject(Dataset.class)) {
×
1547
                List<String> args = Arrays.asList(roleAlias);
×
1548
                String retStringError = BundleUtil.getStringFromBundle("dataverses.api.update.default.contributor.role.failure.role.does.not.have.dataset.permissions", args);
×
1549
                return error(Status.BAD_REQUEST, retStringError);
×
1550
            }
1551

1552
        }
1553

1554
        try {
1555
            Dataverse dv = findDataverseOrDie(dvIdtf);
×
1556
            
1557
            String defaultRoleName = defaultRole == null ? BundleUtil.getStringFromBundle("permission.default.contributor.role.none.name") : defaultRole.getName();
×
1558

1559
            return response(req -> {
×
1560
                execCommand(new UpdateDataverseDefaultContributorRoleCommand(defaultRole, req, dv));
×
1561
                List<String> args = Arrays.asList(dv.getDisplayName(), defaultRoleName);
×
1562
                String retString = BundleUtil.getStringFromBundle("dataverses.api.update.default.contributor.role.success", args);
×
1563
                return ok(retString);
×
1564
            }, getRequestUser(crc));
×
1565

1566
        } catch (WrappedResponse wr) {
×
1567
            return wr.getResponse();
×
1568
        }
1569

1570
    }
1571

1572
    @DELETE
1573
    @AuthRequired
1574
    @Path("{identifier}/groups/{aliasInOwner}")
1575
    public Response deleteGroup(@Context ContainerRequestContext crc,
1576
                                @PathParam("identifier") String dvIdtf,
1577
                                @PathParam("aliasInOwner") String grpAliasInOwner) {
1578
        return response(req -> {
×
1579
            execCommand(new DeleteExplicitGroupCommand(req,
×
1580
                    findExplicitGroupOrDie(findDataverseOrDie(dvIdtf), req, grpAliasInOwner)));
×
1581
            return ok("Group " + dvIdtf + "/" + grpAliasInOwner + " deleted");
×
1582
        }, getRequestUser(crc));
×
1583
    }
1584

1585
    @POST
1586
    @AuthRequired
1587
    @Path("{identifier}/groups/{aliasInOwner}/roleAssignees")
1588
    @Consumes("application/json")
1589
    public Response addRoleAssingees(@Context ContainerRequestContext crc,
1590
                                     List<String> roleAssingeeIdentifiers,
1591
                                     @PathParam("identifier") String dvIdtf,
1592
                                     @PathParam("aliasInOwner") String grpAliasInOwner) {
1593
        return response(req -> ok(
×
1594
                json(
×
1595
                    execCommand(
×
1596
                                new AddRoleAssigneesToExplicitGroupCommand(req,
1597
                                        findExplicitGroupOrDie(findDataverseOrDie(dvIdtf), req, grpAliasInOwner),
×
1598
                                        new TreeSet<>(roleAssingeeIdentifiers))))), getRequestUser(crc));
×
1599
    }
1600

1601
    @PUT
1602
    @AuthRequired
1603
    @Path("{identifier}/groups/{aliasInOwner}/roleAssignees/{roleAssigneeIdentifier: .*}")
1604
    public Response addRoleAssingee(@Context ContainerRequestContext crc,
1605
                                    @PathParam("identifier") String dvIdtf,
1606
                                    @PathParam("aliasInOwner") String grpAliasInOwner,
1607
                                    @PathParam("roleAssigneeIdentifier") String roleAssigneeIdentifier) {
1608
        return addRoleAssingees(crc, Collections.singletonList(roleAssigneeIdentifier), dvIdtf, grpAliasInOwner);
×
1609
    }
1610

1611
    @DELETE
1612
    @AuthRequired
1613
    @Path("{identifier}/groups/{aliasInOwner}/roleAssignees/{roleAssigneeIdentifier: .*}")
1614
    public Response deleteRoleAssingee(@Context ContainerRequestContext crc,
1615
                                       @PathParam("identifier") String dvIdtf,
1616
                                       @PathParam("aliasInOwner") String grpAliasInOwner,
1617
                                       @PathParam("roleAssigneeIdentifier") String roleAssigneeIdentifier) {
1618
        return response(req -> ok(json(execCommand(
×
1619
                new RemoveRoleAssigneesFromExplicitGroupCommand(req,
1620
                        findExplicitGroupOrDie(findDataverseOrDie(dvIdtf), req, grpAliasInOwner),
×
1621
                        Collections.singleton(roleAssigneeIdentifier))))), getRequestUser(crc));
×
1622
    }
1623

1624
    private ExplicitGroup findExplicitGroupOrDie(DvObject dv, DataverseRequest req, String groupIdtf) throws WrappedResponse {
1625
        ExplicitGroup eg = execCommand(new GetExplicitGroupCommand(req, dv, groupIdtf));
×
1626
        if (eg == null) {
×
1627
            throw new WrappedResponse(notFound("Can't find " + groupIdtf + " in dataverse " + dv.getId()));
×
1628
        }
1629
        return eg;
×
1630
    }
1631

1632
    @GET
1633
    @AuthRequired
1634
    @Path("{identifier}/links")
1635
    public Response listLinks(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf) {
1636
        try {
1637
            User u = getRequestUser(crc);
×
1638
            Dataverse dv = findDataverseOrDie(dvIdtf);
×
1639
            if (!u.isSuperuser()) {
×
1640
                return error(Status.FORBIDDEN, "Not a superuser");
×
1641
            }
1642

1643
            List<Dataverse> dvsThisDvHasLinkedToList = dataverseSvc.findDataversesThisIdHasLinkedTo(dv.getId());
×
1644
            JsonArrayBuilder dvsThisDvHasLinkedToBuilder = Json.createArrayBuilder();
×
1645
            for (Dataverse dataverse : dvsThisDvHasLinkedToList) {
×
1646
                dvsThisDvHasLinkedToBuilder.add(dataverse.getAlias());
×
1647
            }
×
1648

1649
            List<Dataverse> dvsThatLinkToThisDvList = dataverseSvc.findDataversesThatLinkToThisDvId(dv.getId());
×
1650
            JsonArrayBuilder dvsThatLinkToThisDvBuilder = Json.createArrayBuilder();
×
1651
            for (Dataverse dataverse : dvsThatLinkToThisDvList) {
×
1652
                dvsThatLinkToThisDvBuilder.add(dataverse.getAlias());
×
1653
            }
×
1654

1655
            List<Dataset> datasetsThisDvHasLinkedToList = dataverseSvc.findDatasetsThisIdHasLinkedTo(dv.getId());
×
1656
            JsonArrayBuilder datasetsThisDvHasLinkedToBuilder = Json.createArrayBuilder();
×
1657
            for (Dataset dataset : datasetsThisDvHasLinkedToList) {
×
1658
                datasetsThisDvHasLinkedToBuilder.add(dataset.getLatestVersion().getTitle());
×
1659
            }
×
1660

1661
            JsonObjectBuilder response = Json.createObjectBuilder();
×
1662
            response.add("dataverses that the " + dv.getAlias() + " dataverse has linked to", dvsThisDvHasLinkedToBuilder);
×
1663
            response.add("dataverses that link to the " + dv.getAlias(), dvsThatLinkToThisDvBuilder);
×
1664
            response.add("datasets that the " + dv.getAlias() + " has linked to", datasetsThisDvHasLinkedToBuilder);
×
1665
            return ok(response);
×
1666

1667
        } catch (WrappedResponse wr) {
×
1668
            return wr.getResponse();
×
1669
        }
1670
    }
1671

1672
    @POST
1673
    @AuthRequired
1674
    @Path("{id}/move/{targetDataverseAlias}")
1675
    public Response moveDataverse(@Context ContainerRequestContext crc, @PathParam("id") String id, @PathParam("targetDataverseAlias") String targetDataverseAlias, @QueryParam("forceMove") Boolean force) {
1676
        try {
1677
            User u = getRequestUser(crc);
×
1678
            Dataverse dv = findDataverseOrDie(id);
×
1679
            Dataverse target = findDataverseOrDie(targetDataverseAlias);
×
1680
            if (target == null) {
×
1681
                return error(Response.Status.BAD_REQUEST, "Target Dataverse not found.");
×
1682
            }
1683
            execCommand(new MoveDataverseCommand(
×
1684
                    createDataverseRequest(u), dv, target, force
×
1685
            ));
1686
            return ok("Dataverse moved successfully");
×
1687
        } catch (WrappedResponse ex) {
×
1688
            return ex.getResponse();
×
1689
        }
1690
    }
1691

1692
    @PUT
1693
    @AuthRequired
1694
    @Path("{linkedDataverseAlias}/link/{linkingDataverseAlias}")
1695
    public Response linkDataverse(@Context ContainerRequestContext crc, @PathParam("linkedDataverseAlias") String linkedDataverseAlias, @PathParam("linkingDataverseAlias") String linkingDataverseAlias) {
1696
        try {
1697
            User u = getRequestUser(crc);
×
1698
            Dataverse linked = findDataverseOrDie(linkedDataverseAlias);
×
1699
            Dataverse linking = findDataverseOrDie(linkingDataverseAlias);
×
1700
            if (linked == null) {
×
1701
                return error(Response.Status.BAD_REQUEST, "Linked Dataverse not found.");
×
1702
            }
1703
            if (linking == null) {
×
1704
                return error(Response.Status.BAD_REQUEST, "Linking Dataverse not found.");
×
1705
            }
1706
            execCommand(new LinkDataverseCommand(
×
1707
                    createDataverseRequest(u), linking, linked
×
1708
            ));
1709
            return ok("Dataverse " + linked.getAlias() + " linked successfully to " + linking.getAlias());
×
1710
        } catch (WrappedResponse ex) {
×
1711
            return ex.getResponse();
×
1712
        }
1713
    }
1714

1715
    @GET
1716
    @AuthRequired
1717
    @Path("{identifier}/userPermissions")
1718
    public Response getUserPermissionsOnDataverse(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf) {
1719
        Dataverse dataverse;
1720
        try {
1721
            dataverse = findDataverseOrDie(dvIdtf);
×
1722
        } catch (WrappedResponse wr) {
×
1723
            return wr.getResponse();
×
1724
        }
×
1725
        User requestUser = getRequestUser(crc);
×
1726
        JsonObjectBuilder jsonObjectBuilder = Json.createObjectBuilder();
×
1727
        jsonObjectBuilder.add("canAddDataverse", permissionService.userOn(requestUser, dataverse).has(Permission.AddDataverse));
×
1728
        jsonObjectBuilder.add("canAddDataset", permissionService.userOn(requestUser, dataverse).has(Permission.AddDataset));
×
1729
        jsonObjectBuilder.add("canViewUnpublishedDataverse", permissionService.userOn(requestUser, dataverse).has(Permission.ViewUnpublishedDataverse));
×
1730
        jsonObjectBuilder.add("canEditDataverse", permissionService.userOn(requestUser, dataverse).has(Permission.EditDataverse));
×
1731
        jsonObjectBuilder.add("canManageDataversePermissions", permissionService.userOn(requestUser, dataverse).has(Permission.ManageDataversePermissions));
×
1732
        jsonObjectBuilder.add("canPublishDataverse", permissionService.userOn(requestUser, dataverse).has(Permission.PublishDataverse));
×
1733
        jsonObjectBuilder.add("canDeleteDataverse", permissionService.userOn(requestUser, dataverse).has(Permission.DeleteDataverse));
×
1734
        return ok(jsonObjectBuilder);
×
1735
    }
1736
}
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