From 075a987b7be96ad2ae6e89280817322f4a3cd859 Mon Sep 17 00:00:00 2001 From: EL HAJJIOUI Nabil <nabil.elhajjioui@smile.fr> Date: Wed, 12 May 2021 09:30:00 +0200 Subject: [PATCH] [VAS] Manage Archive Unit titles --- .../search/common/dto/ObjectData.java | 50 ++++++ .../search/common/dto/ObjectGroupDto.java | 49 ++++++ .../archives/search/common/rest/RestApi.java | 1 + .../ArchiveSearchExternalRestClient.java | 14 +- .../ArchivesSearchExternalController.java | 14 +- .../ArchivesSearchExternalService.java | 8 +- .../client/ArchiveInternalRestClient.java | 12 +- .../rest/ArchiveSearchInternalController.java | 15 +- .../service/ArchiveSearchInternalService.java | 33 ++-- .../ArchiveSearchInternalServiceTest.java | 5 +- .../api/model/ObjectQualifierTypeEnum.java | 65 +++++++ .../search/rest/ArchivesSearchController.java | 21 ++- .../search/service/ArchivesSearchService.java | 54 +++++- .../service/ArchivesSearchServiceTest.java | 158 ++++++++++++++++++ .../data/vitam_got_binarymaster.json | 76 +++++++++ ...am_got_binarymaster_multiple_versions.json | 102 +++++++++++ ...tam_got_binarymaster_without_fileInfo.json | 72 ++++++++ .../data/vitam_got_dissemination.json | 76 +++++++++ .../resources/data/vitam_got_physical.json | 84 ++++++++++ .../archive-preview.component.ts | 4 +- .../src/app/archive/archive.service.ts | 36 +++- .../src/app/archive/models/object-data.ts | 42 +++++ .../src/app/core/api/archive-api.service.ts | 15 +- 23 files changed, 958 insertions(+), 48 deletions(-) create mode 100644 api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dto/ObjectData.java create mode 100644 api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dto/ObjectGroupDto.java create mode 100644 commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/model/ObjectQualifierTypeEnum.java create mode 100644 ui/ui-archive-search/src/test/resources/data/vitam_got_binarymaster.json create mode 100644 ui/ui-archive-search/src/test/resources/data/vitam_got_binarymaster_multiple_versions.json create mode 100644 ui/ui-archive-search/src/test/resources/data/vitam_got_binarymaster_without_fileInfo.json create mode 100644 ui/ui-archive-search/src/test/resources/data/vitam_got_dissemination.json create mode 100644 ui/ui-archive-search/src/test/resources/data/vitam_got_physical.json create mode 100644 ui/ui-frontend/projects/archive-search/src/app/archive/models/object-data.ts diff --git a/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dto/ObjectData.java b/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dto/ObjectData.java new file mode 100644 index 000000000..4783c8bfa --- /dev/null +++ b/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dto/ObjectData.java @@ -0,0 +1,50 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2015-2020) + * + * contact.vitam@culture.gouv.fr + * + * This software is a computer program whose purpose is to implement a digital archiving back-office system managing + * high volumetry securely and efficiently. + * + * This software is governed by the CeCILL 2.1 license under French law and abiding by the rules of distribution of free + * software. You can use, modify and/ or redistribute the software under the terms of the CeCILL 2.1 license as + * circulated by CEA, CNRS and INRIA at the following URL "https://cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license, + * users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the + * successive licensors have only limited liability. + * + * In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or + * developing or reproducing the software by the user in light of its specific status of free software, that may mean + * that it is complicated to manipulate, and that also therefore means that it is reserved for developers and + * experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the + * software's suitability as regards their requirements in conditions enabling the security of their systems and/or data + * to be ensured and, more generally, to use and operate it in the same conditions as regards security. + * + * The fact that you are presently reading this means that you have had knowledge of the CeCILL 2.1 license and that you + * accept its terms. + */ +package fr.gouv.vitamui.archives.search.common.dto; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.springframework.core.io.Resource; + +import java.io.Serializable; + +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) +public class ObjectData implements Serializable { + private Resource resource; + private String filename; + /** + * mimeType to use to get the exacte vitam mimetype + **/ + private String mimeType; +} diff --git a/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dto/ObjectGroupDto.java b/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dto/ObjectGroupDto.java new file mode 100644 index 000000000..ecaaffd46 --- /dev/null +++ b/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dto/ObjectGroupDto.java @@ -0,0 +1,49 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2015-2020) + * contact.vitam@culture.gouv.fr + * + * This software is a computer program whose purpose is to implement a digital archiving back-office system managing + * high volumetry securely and efficiently. + * + * This software is governed by the CeCILL 2.1 license under French law and abiding by the rules of distribution of free + * software. You can use, modify and/ or redistribute the software under the terms of the CeCILL 2.1 license as + * circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license, + * users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the + * successive licensors have only limited liability. + * + * In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or + * developing or reproducing the software by the user in light of its specific status of free software, that may mean + * that it is complicated to manipulate, and that also therefore means that it is reserved for developers and + * experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the + * software's suitability as regards their requirements in conditions enabling the security of their systems and/or data + * to be ensured and, more generally, to use and operate it in the same conditions as regards security. + * + * The fact that you are presently reading this means that you have had knowledge of the CeCILL 2.1 license and that you + * accept its terms. + */ + +package fr.gouv.vitamui.archives.search.common.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import fr.gouv.vitamui.commons.api.domain.IdDto; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import java.io.Serializable; + + +@JsonInclude(JsonInclude.Include.NON_NULL) +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class ObjectGroupDto extends IdDto implements Serializable { + private VitamUIArchiveUnitResponseDto archives; + +} diff --git a/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/rest/RestApi.java b/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/rest/RestApi.java index e20413cef..02e7a2f2a 100644 --- a/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/rest/RestApi.java +++ b/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/rest/RestApi.java @@ -36,4 +36,5 @@ public class RestApi { public static final String DOWNLOAD_ARCHIVE_UNIT = "/downloadobjectfromunit"; public static final String ARCHIVE_UNIT_INFO = "/archiveunit"; public static final String SEARCH_CRITERIA_HISTORY = "/searchcriteriahistory"; + public static final String OBJECTGROUP = "/object"; } diff --git a/api/api-archive-search/archive-search-external-client/src/main/java/fr/gouv/vitamui/archives/search/external/client/ArchiveSearchExternalRestClient.java b/api/api-archive-search/archive-search-external-client/src/main/java/fr/gouv/vitamui/archives/search/external/client/ArchiveSearchExternalRestClient.java index cb714fd40..ae43976e6 100644 --- a/api/api-archive-search/archive-search-external-client/src/main/java/fr/gouv/vitamui/archives/search/external/client/ArchiveSearchExternalRestClient.java +++ b/api/api-archive-search/archive-search-external-client/src/main/java/fr/gouv/vitamui/archives/search/external/client/ArchiveSearchExternalRestClient.java @@ -38,12 +38,14 @@ import fr.gouv.vitamui.commons.rest.client.BasePaginatingAndSortingRestClient; import fr.gouv.vitamui.commons.rest.client.ExternalHttpContext; import fr.gouv.vitamui.commons.vitam.api.dto.ResultsDto; import fr.gouv.vitamui.commons.vitam.api.dto.VitamUISearchResponseDto; +import org.apache.http.client.utils.URIBuilder; import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.io.Resource; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; @@ -128,9 +130,17 @@ public class ArchiveSearchExternalRestClient return restTemplate.exchange(uriBuilder.build(id), HttpMethod.GET, request, ResultsDto.class); } - public ResponseEntity<Resource> downloadObjectFromUnit(String id, ExternalHttpContext context) { + public ResponseEntity<ResultsDto> findObjectById(String id, ExternalHttpContext context) { final UriComponentsBuilder uriBuilder = - UriComponentsBuilder.fromHttpUrl(getUrl() + RestApi.DOWNLOAD_ARCHIVE_UNIT + CommonConstants.PATH_ID); + UriComponentsBuilder.fromHttpUrl(getUrl() + RestApi.OBJECTGROUP + CommonConstants.PATH_ID); + final HttpEntity<?> request = new HttpEntity<>(buildHeaders(context)); + return restTemplate.exchange(uriBuilder.build(id), HttpMethod.GET, request, ResultsDto.class); + + } + + public ResponseEntity<Resource> downloadObjectFromUnit(String id, String usage, Integer version, ExternalHttpContext context) { + final UriComponentsBuilder uriBuilder = + UriComponentsBuilder.fromHttpUrl(getUrl() + RestApi.DOWNLOAD_ARCHIVE_UNIT + CommonConstants.PATH_ID + "?usage=" + usage + "&version=" + version); final HttpEntity<?> request = new HttpEntity<>(buildHeaders(context)); return restTemplate.exchange(uriBuilder.build(id), HttpMethod.GET, request, Resource.class); } diff --git a/api/api-archive-search/archive-search-external/src/main/java/fr/gouv/vitamui/archives/search/external/server/rest/ArchivesSearchExternalController.java b/api/api-archive-search/archive-search-external/src/main/java/fr/gouv/vitamui/archives/search/external/server/rest/ArchivesSearchExternalController.java index 1f388b9b9..a7173a86d 100644 --- a/api/api-archive-search/archive-search-external/src/main/java/fr/gouv/vitamui/archives/search/external/server/rest/ArchivesSearchExternalController.java +++ b/api/api-archive-search/archive-search-external/src/main/java/fr/gouv/vitamui/archives/search/external/server/rest/ArchivesSearchExternalController.java @@ -50,6 +50,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @@ -90,10 +91,11 @@ public class ArchivesSearchExternalController { @GetMapping(RestApi.DOWNLOAD_ARCHIVE_UNIT + CommonConstants.PATH_ID) @Secured(ServicesData.ROLE_GET_ARCHIVE) - public ResponseEntity<Resource> downloadObjectFromUnit(final @PathVariable("id") String id) { + public ResponseEntity<Resource> downloadObjectFromUnit(final @PathVariable("id") String id, + final @RequestParam("usage") String usage, final @RequestParam("version") Integer version) { LOGGER.info("Download the Archive Unit Object with id {} ", id); ParameterChecker.checkParameter("The Identifier is a mandatory parameter: ", id); - return archivesSearchExternalService.downloadObjectFromUnit(id); + return archivesSearchExternalService.downloadObjectFromUnit(id, usage, version); } @GetMapping(RestApi.ARCHIVE_UNIT_INFO + CommonConstants.PATH_ID) @@ -104,6 +106,14 @@ public class ArchivesSearchExternalController { return archivesSearchExternalService.findUnitById(id); } + @GetMapping(RestApi.OBJECTGROUP + CommonConstants.PATH_ID) + @Secured(ServicesData.ROLE_GET_ARCHIVE) + public ResponseEntity<ResultsDto> findObjectById(final @PathVariable("id") String id) { + LOGGER.info("Find a ObjectGroup by id {} ", id); + ParameterChecker.checkParameter("The Identifier is a mandatory parameter: ", id); + return archivesSearchExternalService.findObjectById(id); + } + @PostMapping(RestApi.EXPORT_CSV_SEARCH_PATH) @Secured(ServicesData.ROLE_GET_ARCHIVE) public Resource exportCsvArchiveUnitsByCriteria(final @RequestBody SearchCriteriaDto query) { diff --git a/api/api-archive-search/archive-search-external/src/main/java/fr/gouv/vitamui/archives/search/external/server/service/ArchivesSearchExternalService.java b/api/api-archive-search/archive-search-external/src/main/java/fr/gouv/vitamui/archives/search/external/server/service/ArchivesSearchExternalService.java index fe710fc66..ede87fb78 100644 --- a/api/api-archive-search/archive-search-external/src/main/java/fr/gouv/vitamui/archives/search/external/server/service/ArchivesSearchExternalService.java +++ b/api/api-archive-search/archive-search-external/src/main/java/fr/gouv/vitamui/archives/search/external/server/service/ArchivesSearchExternalService.java @@ -79,12 +79,16 @@ public class ArchivesSearchExternalService extends AbstractResourceClientService return archiveInternalRestClient.findUnitById(id, getInternalHttpContext()); } + public ResponseEntity<ResultsDto> findObjectById(String id) { + return archiveInternalRestClient.findObjectById(id, getInternalHttpContext()); + } + public VitamUISearchResponseDto getFilingHoldingScheme() { return archiveInternalRestClient.getFilingHoldingScheme(getInternalHttpContext()); } - public ResponseEntity<Resource> downloadObjectFromUnit(String id) { - return archiveInternalRestClient.downloadObjectFromUnit(id, getInternalHttpContext()); + public ResponseEntity<Resource> downloadObjectFromUnit(String id, String usage, Integer version) { + return archiveInternalRestClient.downloadObjectFromUnit(id, usage, version, getInternalHttpContext()); } public Resource exportCsvArchiveUnitsByCriteria(final SearchCriteriaDto query) { diff --git a/api/api-archive-search/archive-search-internal-client/src/main/java/fr/gouv/archive/internal/client/ArchiveInternalRestClient.java b/api/api-archive-search/archive-search-internal-client/src/main/java/fr/gouv/archive/internal/client/ArchiveInternalRestClient.java index 9ce5b8ebd..88ca8035f 100644 --- a/api/api-archive-search/archive-search-internal-client/src/main/java/fr/gouv/archive/internal/client/ArchiveInternalRestClient.java +++ b/api/api-archive-search/archive-search-internal-client/src/main/java/fr/gouv/archive/internal/client/ArchiveInternalRestClient.java @@ -124,11 +124,12 @@ public class ArchiveInternalRestClient return headers; } - public ResponseEntity<Resource> downloadObjectFromUnit(String id, final InternalHttpContext context) { + public ResponseEntity<Resource> downloadObjectFromUnit(String id, final String usage, Integer version, final InternalHttpContext context) { final UriComponentsBuilder uriBuilder = - UriComponentsBuilder.fromHttpUrl(getUrl() + RestApi.DOWNLOAD_ARCHIVE_UNIT + CommonConstants.PATH_ID); + UriComponentsBuilder.fromHttpUrl(getUrl() + RestApi.DOWNLOAD_ARCHIVE_UNIT + CommonConstants.PATH_ID + "?usage=" + usage + "&version=" + version); final HttpEntity<?> request = new HttpEntity<>(buildHeaders(context)); return restTemplate.exchange(uriBuilder.build(id), HttpMethod.GET, request, Resource.class); + } public ResponseEntity<ResultsDto> findUnitById(String id, final InternalHttpContext context) { @@ -138,6 +139,13 @@ public class ArchiveInternalRestClient return restTemplate.exchange(uriBuilder.build(id), HttpMethod.GET, request, ResultsDto.class); } + public ResponseEntity<ResultsDto> findObjectById(String id, final InternalHttpContext context) { + final UriComponentsBuilder uriBuilder = + UriComponentsBuilder.fromHttpUrl(getUrl() + RestApi.OBJECTGROUP + CommonConstants.PATH_ID); + final HttpEntity<?> request = new HttpEntity<>(buildHeaders(context)); + return restTemplate.exchange(uriBuilder.build(id), HttpMethod.GET, request, ResultsDto.class); + } + public Resource exportCsvArchiveUnitsByCriteria(final SearchCriteriaDto query, final InternalHttpContext context) { LOGGER.info("Calling exportCsvArchiveUnitsByCriteria with query {} ", query); diff --git a/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/rest/ArchiveSearchInternalController.java b/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/rest/ArchiveSearchInternalController.java index 5bc0ae4da..11648c1dd 100644 --- a/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/rest/ArchiveSearchInternalController.java +++ b/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/rest/ArchiveSearchInternalController.java @@ -62,11 +62,13 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.ws.rs.core.Response; import java.io.IOException; import java.io.InputStream; +import java.util.List; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.in; import static fr.gouv.vitam.common.database.builder.query.VitamFieldsHelper.unitType; @@ -152,8 +154,19 @@ public class ArchiveSearchInternalController { return archiveInternalService.findUnitById(id,vitamContext); } + @GetMapping(RestApi.OBJECTGROUP + CommonConstants.PATH_ID) + public ResultsDto findObjectById(final @PathVariable("id") String id, + @RequestHeader(value = CommonConstants.X_ACCESS_CONTRACT_ID_HEADER) final String accessContractId) + throws VitamClientException { + LOGGER.info("Get ObjectGroup By id : {}", id); + ParameterChecker.checkParameter("The identifier, the accessContract Id are mandatory parameters: ", id, accessContractId); + VitamContext vitamContext = securityService.buildVitamContext(securityService.getTenantIdentifier(), accessContractId); + return archiveInternalService.findObjectById(id,vitamContext); + } + @GetMapping(RestApi.DOWNLOAD_ARCHIVE_UNIT + CommonConstants.PATH_ID) public ResponseEntity<Resource> downloadObjectFromUnit(final @PathVariable("id") String id, + final @RequestParam("usage") String usage, final @RequestParam("version") Integer version, @RequestHeader(value = CommonConstants.X_ACCESS_CONTRACT_ID_HEADER) final String accessContractId) throws VitamClientException { @@ -164,7 +177,7 @@ public class ArchiveSearchInternalController { LOGGER.info("Download Archive Unit Object with id {}", id); final VitamContext vitamContext = securityService.buildVitamContext(securityService.getTenantIdentifier(), accessContractId); - Response response = archiveInternalService.downloadObjectFromUnit(id, vitamContext); + Response response = archiveInternalService.downloadObjectFromUnit(id, usage, version, vitamContext); Object entity = response.getEntity(); if (entity instanceof InputStream) { Resource resource = new InputStreamResource((InputStream) entity); diff --git a/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/service/ArchiveSearchInternalService.java b/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/service/ArchiveSearchInternalService.java index 54a74ec66..135b05022 100644 --- a/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/service/ArchiveSearchInternalService.java +++ b/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/service/ArchiveSearchInternalService.java @@ -52,7 +52,9 @@ import fr.gouv.vitamui.commons.api.exception.RequestEntityTooLargeException; import fr.gouv.vitamui.commons.api.logger.VitamUILogger; import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory; import fr.gouv.vitamui.commons.vitam.api.access.UnitService; +import fr.gouv.vitamui.commons.vitam.api.dto.QualifiersDto; import fr.gouv.vitamui.commons.vitam.api.dto.ResultsDto; +import fr.gouv.vitamui.commons.vitam.api.dto.VersionsDto; import fr.gouv.vitamui.commons.vitam.api.dto.VitamUISearchResponseDto; import org.apache.commons.lang.StringUtils; import org.springframework.beans.BeanUtils; @@ -187,8 +189,7 @@ public class ArchiveSearchInternalService { public ResultsDto findUnitById(String id, VitamContext vitamContext) throws VitamClientException { try { - LOGGER.info("Archive Unit Infos {}", - unitService.findUnitById(id, vitamContext).toJsonNode().get(ARCHIVE_UNIT_DETAILS)); + LOGGER.info("Archive Unit Id : {}", id); String re = StringUtils .chop(unitService.findUnitById(id, vitamContext).toJsonNode().get(ARCHIVE_UNIT_DETAILS).toString() .substring(1)); @@ -199,23 +200,27 @@ public class ArchiveSearchInternalService { } } - private String getUsage(String id, VitamContext vitamContext) throws VitamClientException { - return unitService.findObjectMetadataById(id, vitamContext).toJsonNode().findValue(ARCHIVE_UNIT_USAGE) - .textValue(); - } - - private int getVersion(String id, VitamContext vitamContext) throws VitamClientException { - String result = unitService.findObjectMetadataById(id, vitamContext).toJsonNode() - .findValue(ARCHIVE_UNIT_VERSION).textValue(); - return Integer.valueOf(result.split("_")[1]); + public ResultsDto findObjectById(String id, VitamContext vitamContext) throws VitamClientException { + try { + LOGGER.info("Get Object Group"); + String re = StringUtils + .chop( + unitService.findObjectMetadataById(id, vitamContext).toJsonNode() + .get(ARCHIVE_UNIT_DETAILS) + .toString() + .substring(1)); + return objectMapper.readValue(re, ResultsDto.class); + } catch (JsonProcessingException e) { + LOGGER.error("Can not get the archive unit {} ", e); + throw new VitamClientException("Unable to find the UA", e); + } } - public Response downloadObjectFromUnit(String id, final VitamContext vitamContext) + public Response downloadObjectFromUnit(String id, String usage, Integer version, final VitamContext vitamContext) throws VitamClientException { - LOGGER.info("Download Archive Unit Object with id {} ", id); return unitService - .getObjectStreamByUnitId(id, getUsage(id, vitamContext), getVersion(id, vitamContext), vitamContext); + .getObjectStreamByUnitId(id, usage, version, vitamContext); } /** diff --git a/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/service/ArchiveSearchInternalServiceTest.java b/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/service/ArchiveSearchInternalServiceTest.java index f2f930c9d..688167b68 100644 --- a/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/service/ArchiveSearchInternalServiceTest.java +++ b/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/service/ArchiveSearchInternalServiceTest.java @@ -37,7 +37,9 @@ package fr.gouv.vitamui.archive.internal.server.service; +import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.io.ByteStreams; @@ -68,7 +70,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @@ -142,6 +143,4 @@ public class ArchiveSearchInternalServiceTest { return RequestResponseOK .getFromJsonNode(objectMapper.readValue(ByteStreams.toByteArray(inputStream), JsonNode.class)); } - - } diff --git a/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/model/ObjectQualifierTypeEnum.java b/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/model/ObjectQualifierTypeEnum.java new file mode 100644 index 000000000..a138dbaad --- /dev/null +++ b/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/model/ObjectQualifierTypeEnum.java @@ -0,0 +1,65 @@ +/** + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +package fr.gouv.vitamui.commons.vitam.api.model; + +import fr.gouv.vitamui.commons.api.exception.BadRequestException; + +public enum ObjectQualifierTypeEnum { + BINARYMASTER("BinaryMaster"), + DISSEMINATION("Dissemination"), + THUMBNAIL("Thumbnail"), + PHYSICALMASTER("PhysicalMaster"); + + private final String value; + + ObjectQualifierTypeEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static ObjectQualifierTypeEnum fromValue(String value) { + for (ObjectQualifierTypeEnum objectQualifierTypeEnum : ObjectQualifierTypeEnum.values()) { + if (objectQualifierTypeEnum.getValue().equalsIgnoreCase(value)) { + return objectQualifierTypeEnum; + } + } + throw new BadRequestException("the object qualifier type " + value + " specified is not a valid one"); + } +} diff --git a/ui/ui-archive-search/src/main/java/fr/gouv/vitamui/archives/search/rest/ArchivesSearchController.java b/ui/ui-archive-search/src/main/java/fr/gouv/vitamui/archives/search/rest/ArchivesSearchController.java index c3f62aa6c..8cda552ec 100644 --- a/ui/ui-archive-search/src/main/java/fr/gouv/vitamui/archives/search/rest/ArchivesSearchController.java +++ b/ui/ui-archive-search/src/main/java/fr/gouv/vitamui/archives/search/rest/ArchivesSearchController.java @@ -30,6 +30,7 @@ import fr.gouv.vitamui.archives.search.common.dto.ArchiveUnitsDto; import fr.gouv.vitamui.archives.search.common.dto.SearchCriteriaDto; import fr.gouv.vitamui.archives.search.common.dto.VitamUIArchiveUnitResponseDto; import fr.gouv.vitamui.archives.search.common.rest.RestApi; +import fr.gouv.vitamui.archives.search.common.dto.ObjectData; import fr.gouv.vitamui.archives.search.service.ArchivesSearchService; import fr.gouv.vitamui.commons.api.CommonConstants; import fr.gouv.vitamui.commons.api.ParameterChecker; @@ -40,6 +41,7 @@ import fr.gouv.vitamui.commons.vitam.api.dto.ResultsDto; import fr.gouv.vitamui.commons.vitam.api.dto.VitamUISearchResponseDto; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.http.HttpStatus; @@ -107,19 +109,30 @@ public class ArchivesSearchController extends AbstractUiRestController { return archivesSearchService.findUnitById(id, buildUiHttpContext()); } + @ApiOperation(value = "Find the Object Group by identifier") + @GetMapping(RestApi.OBJECTGROUP + CommonConstants.PATH_ID) + @ResponseStatus(HttpStatus.OK) + public ResponseEntity<ResultsDto> findObjectById(final @PathVariable("id") String id){ + ParameterChecker.checkParameter("The Identifier is a mandatory parameter: ", id); + LOGGER.debug("Find the Object Group with Identifier {}", id); + return archivesSearchService.findObjectById(id, buildUiHttpContext()); + } + @ApiOperation(value = "Download Object from the Archive Unit ") @GetMapping(RestApi.DOWNLOAD_ARCHIVE_UNIT + CommonConstants.PATH_ID) @ResponseStatus(HttpStatus.OK) public ResponseEntity<Resource> downloadObjectFromUnit(final @PathVariable("id") String id) { ParameterChecker.checkParameter("The Identifier is a mandatory parameter: ", id); LOGGER.debug("Download the Archive Unit Object with ID {}", id); - Resource body = archivesSearchService.downloadObjectFromUnit(id, buildUiHttpContext()).getBody(); + ObjectData objectData = archivesSearchService.downloadObjectFromUnit(id, buildUiHttpContext()); + String headerValues = StringUtils.isNotEmpty(objectData.getFilename()) ? "attachment;filename=" + objectData.getFilename() + : "attachment"; return ResponseEntity.ok() - .contentType(MediaType.APPLICATION_OCTET_STREAM).header("Content-Disposition", "attachment") - .body(body); + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .header("Content-Disposition", headerValues) + .body(objectData.getResource()); } - @ApiOperation(value = "export into csv format archive units by criteria") @PostMapping(RestApi.EXPORT_CSV_SEARCH_PATH) @Consumes(MediaType.APPLICATION_JSON_VALUE) diff --git a/ui/ui-archive-search/src/main/java/fr/gouv/vitamui/archives/search/service/ArchivesSearchService.java b/ui/ui-archive-search/src/main/java/fr/gouv/vitamui/archives/search/service/ArchivesSearchService.java index e0d161254..5888fb1e3 100644 --- a/ui/ui-archive-search/src/main/java/fr/gouv/vitamui/archives/search/service/ArchivesSearchService.java +++ b/ui/ui-archive-search/src/main/java/fr/gouv/vitamui/archives/search/service/ArchivesSearchService.java @@ -28,20 +28,28 @@ package fr.gouv.vitamui.archives.search.service; import fr.gouv.vitamui.archives.search.common.dto.ArchiveUnitsDto; import fr.gouv.vitamui.archives.search.common.dto.SearchCriteriaDto; +import fr.gouv.vitamui.archives.search.common.dto.ObjectData; import fr.gouv.vitamui.archives.search.external.client.ArchiveSearchExternalRestClient; import fr.gouv.vitamui.archives.search.external.client.ArchiveSearchExternalWebClient; import fr.gouv.vitamui.commons.api.logger.VitamUILogger; import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory; import fr.gouv.vitamui.commons.rest.client.ExternalHttpContext; +import fr.gouv.vitamui.commons.vitam.api.dto.QualifiersDto; import fr.gouv.vitamui.commons.vitam.api.dto.ResultsDto; +import fr.gouv.vitamui.commons.vitam.api.dto.VersionsDto; import fr.gouv.vitamui.commons.vitam.api.dto.VitamUISearchResponseDto; +import fr.gouv.vitamui.commons.vitam.api.model.ObjectQualifierTypeEnum; import fr.gouv.vitamui.ui.commons.service.AbstractPaginateService; import fr.gouv.vitamui.ui.commons.service.CommonService; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import java.util.List; +import java.util.Objects; + /** * UI @@ -85,9 +93,16 @@ public class ArchivesSearchService extends AbstractPaginateService<ArchiveUnitsD return archiveSearchExternalRestClient.getFilingHoldingScheme(context); } - public ResponseEntity<Resource> downloadObjectFromUnit(String id, ExternalHttpContext context) { + public ObjectData downloadObjectFromUnit(String id, ExternalHttpContext context) { LOGGER.info("Download the Archive Unit Object with id {}", id); - return archiveSearchExternalRestClient.downloadObjectFromUnit(id, context); + ObjectData objectData = new ObjectData(); + ResultsDto got = findObjectById(id, context).getBody(); + String usage = getUsage(Objects.requireNonNull(got), objectData); + ResponseEntity<Resource> resourceResponseEntity = + archiveSearchExternalRestClient.downloadObjectFromUnit(id, usage, getVersion(got.getQualifiers(), usage), context); + Resource resource = resourceResponseEntity.getBody(); + objectData.setResource(resource); + return objectData; } public ResponseEntity<ResultsDto> findUnitById(String id, ExternalHttpContext context) { @@ -95,10 +110,45 @@ public class ArchivesSearchService extends AbstractPaginateService<ArchiveUnitsD return archiveSearchExternalRestClient.findUnitById(id, context); } + public ResponseEntity<ResultsDto> findObjectById(String id, ExternalHttpContext context) { + LOGGER.info("Get the Object Group with Identifier {}", id); + return archiveSearchExternalRestClient.findObjectById(id, context); + } public ResponseEntity<Resource> exportCsvArchiveUnitsByCriteria(final SearchCriteriaDto searchQuery, ExternalHttpContext context) { LOGGER.info("export search archives Units by criteria into csv format with criteria {}", searchQuery); return archiveSearchExternalRestClient.exportCsvArchiveUnitsByCriteria(searchQuery, context); } + public String getUsage(ResultsDto got, ObjectData objectData) { + List<QualifiersDto> qualifiers = got.getQualifiers(); + for (QualifiersDto qualifier : qualifiers) { + if (qualifier.getQualifier().equals(ObjectQualifierTypeEnum.BINARYMASTER.getValue())) { + return setObjectData(objectData, qualifier, ObjectQualifierTypeEnum.BINARYMASTER); + } + if (qualifier.getQualifier().equals(ObjectQualifierTypeEnum.DISSEMINATION.getValue())) { + return setObjectData(objectData, qualifier, ObjectQualifierTypeEnum.DISSEMINATION); + } + if (qualifier.getQualifier().equals(ObjectQualifierTypeEnum.THUMBNAIL.getValue())) { + return setObjectData(objectData, qualifier, ObjectQualifierTypeEnum.THUMBNAIL); + } + } + return null; + } + + @NotNull + private String setObjectData(ObjectData objectData, QualifiersDto qualifier, ObjectQualifierTypeEnum objectQualifierTypeEnum) { + if(qualifier.getVersions().get(0).getFileInfoModel() != null) { + String filename = qualifier.getVersions().get(0).getFileInfoModel().getFilename(); + objectData.setFilename(filename); + } + objectData.setMimeType(qualifier.getVersions().get(0).getFormatIdentification().getMimeType()); + return objectQualifierTypeEnum.getValue(); + } + + public Integer getVersion(List<QualifiersDto> qualifiers, String usage) { + List<VersionsDto> versions = + qualifiers.stream().filter(q -> q.getQualifier().equals(usage)).findFirst().get().getVersions(); + return Integer.parseInt(versions.get(0).getDataObjectVersion().split("_")[1]); + } } diff --git a/ui/ui-archive-search/src/test/java/fr/gouv/vitamui/archives/search/service/ArchivesSearchServiceTest.java b/ui/ui-archive-search/src/test/java/fr/gouv/vitamui/archives/search/service/ArchivesSearchServiceTest.java index 7cfd90c5d..0d527f828 100644 --- a/ui/ui-archive-search/src/test/java/fr/gouv/vitamui/archives/search/service/ArchivesSearchServiceTest.java +++ b/ui/ui-archive-search/src/test/java/fr/gouv/vitamui/archives/search/service/ArchivesSearchServiceTest.java @@ -26,13 +26,26 @@ package fr.gouv.vitamui.archives.search.service; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.io.ByteStreams; +import fr.gouv.vitam.common.exception.InvalidParseOperationException; +import fr.gouv.vitam.common.exception.VitamClientException; +import fr.gouv.vitam.common.model.RequestResponse; +import fr.gouv.vitam.common.model.RequestResponseOK; import fr.gouv.vitamui.archives.search.common.dto.SearchCriteriaDto; +import fr.gouv.vitamui.archives.search.common.dto.ObjectData; import fr.gouv.vitamui.archives.search.external.client.ArchiveSearchExternalRestClient; import fr.gouv.vitamui.archives.search.external.client.ArchiveSearchExternalWebClient; import fr.gouv.vitamui.commons.rest.client.ExternalHttpContext; import fr.gouv.vitamui.commons.test.utils.ServerIdentityConfigurationBuilder; +import fr.gouv.vitamui.commons.vitam.api.access.UnitService; +import fr.gouv.vitamui.commons.vitam.api.dto.ResultsDto; import fr.gouv.vitamui.commons.vitam.api.dto.VitamUISearchResponseDto; +import fr.gouv.vitamui.commons.vitam.api.model.ObjectQualifierTypeEnum; import fr.gouv.vitamui.ui.commons.service.CommonService; +import org.apache.commons.lang.StringUtils; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -46,12 +59,21 @@ import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringRunner; import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; +import static org.junit.Assert.assertEquals; @RunWith(SpringRunner.class) public class ArchivesSearchServiceTest { public final String ARCHIVE_UNITS_RESULTS_CSV = "data/vitam_archive_units_response.csv"; + public final String GOT_PHYSICAL = "data/vitam_got_physical.json"; + public final String GOT_DISSEMINATION = "data/vitam_got_dissemination.json"; + public final String GOT_BINARYMASTER_MULTI_QUALIFIERS = "data/vitam_got_binarymaster_multiple_versions.json"; + public final String GOT_BINARYMASTER = "data/vitam_got_binarymaster.json"; private ArchivesSearchService archivesSearchService; @@ -64,6 +86,9 @@ public class ArchivesSearchServiceTest { @Mock private CommonService commonService; + @Mock + private UnitService unitService; + @Before public void init() { archivesSearchService = new ArchivesSearchService(commonService, archiveSearchExternalRestClient, @@ -95,4 +120,137 @@ public class ArchivesSearchServiceTest { (new ResponseEntity<>(resource, HttpStatus.OK))); Assert.assertNotNull(archivesSearchService.exportCsvArchiveUnitsByCriteria(query, context)); } + + @Test + public void should_return_binary_master_file_info() + throws IOException, InvalidParseOperationException, VitamClientException { + // Given + ObjectData objectData = new ObjectData(); + RequestResponse<JsonNode> jsonNodeRequestResponse = buildGot(GOT_BINARYMASTER); + ResultsDto resultsDto = buildResults(jsonNodeRequestResponse); + // When + when(unitService.findObjectMetadataById(any(), any())) + .thenReturn(jsonNodeRequestResponse); + when(archivesSearchService.findObjectById(any(), any())) + .thenReturn(ResponseEntity.of(Optional.of(resultsDto))); + String usage = archivesSearchService.getUsage(resultsDto, objectData); + + // Then + assertEquals(usage, "BinaryMaster"); + assertEquals(objectData.getFilename(), "this_is_a_BinaryMaster.txt"); + } + + + @Test + public void should_return_version() + throws VitamClientException, IOException, InvalidParseOperationException { + // Given + ObjectData objectData = new ObjectData(); + RequestResponse<JsonNode> jsonNodeRequestResponse = buildGot(GOT_BINARYMASTER); + ResultsDto resultsDto = buildResults(jsonNodeRequestResponse); + // When + when(unitService.findObjectMetadataById(any(), any())) + .thenReturn(jsonNodeRequestResponse); + when(archivesSearchService.findObjectById(any(), any())) + .thenReturn(ResponseEntity.of(Optional.of(resultsDto))); + String usage = archivesSearchService.getUsage(resultsDto, objectData); + int version = archivesSearchService.getVersion(resultsDto.getQualifiers(), usage); + + // Then + assertEquals(usage, ObjectQualifierTypeEnum.BINARYMASTER.getValue()); + assertEquals(version, 1); + } + + @Test + public void should_return_multi_version() + throws VitamClientException, IOException, InvalidParseOperationException { + // Given + ObjectData objectData = new ObjectData(); + RequestResponse<JsonNode> jsonNodeRequestResponse = buildGot(GOT_BINARYMASTER_MULTI_QUALIFIERS); + ResultsDto resultsDto = buildResults(jsonNodeRequestResponse); + // When + when(unitService.findObjectMetadataById(any(), any())) + .thenReturn(jsonNodeRequestResponse); + when(archivesSearchService.findObjectById(any(), any())) + .thenReturn(ResponseEntity.of(Optional.of(resultsDto))); + String usage = archivesSearchService.getUsage(resultsDto, objectData); + int version = archivesSearchService.getVersion(resultsDto.getQualifiers(), usage); + + // Then + assertEquals(usage, ObjectQualifierTypeEnum.BINARYMASTER.getValue()); + assertEquals(version, 1); + } + + @Test + public void should_return_dissemination() + throws VitamClientException, IOException, InvalidParseOperationException { + // Given + ObjectData objectData = new ObjectData(); + RequestResponse<JsonNode> jsonNodeRequestResponse = buildGot(GOT_DISSEMINATION); + ResultsDto resultsDto = buildResults(jsonNodeRequestResponse); + // When + when(unitService.findObjectMetadataById(any(), any())) + .thenReturn(jsonNodeRequestResponse); + when(archivesSearchService.findObjectById(any(), any())) + .thenReturn(ResponseEntity.of(Optional.of(resultsDto))); + String usage = archivesSearchService.getUsage(resultsDto, objectData); + int version = archivesSearchService.getVersion(resultsDto.getQualifiers(), usage); + + // Then + assertEquals(usage, ObjectQualifierTypeEnum.DISSEMINATION.getValue()); + assertEquals(version, 1); + } + + @Test + public void should_return_dissemination_file_info() + throws IOException, InvalidParseOperationException, VitamClientException { + // Given + ObjectData objectData = new ObjectData(); + RequestResponse<JsonNode> jsonNodeRequestResponse = buildGot(GOT_DISSEMINATION); + ResultsDto resultsDto = buildResults(jsonNodeRequestResponse); + // When + when(unitService.findObjectMetadataById(any(), any())) + .thenReturn(jsonNodeRequestResponse); + when(archivesSearchService.findObjectById(any(), any())) + .thenReturn(ResponseEntity.of(Optional.of(resultsDto))); + String usage = archivesSearchService.getUsage(resultsDto, objectData); + + // Then + assertEquals(usage, ObjectQualifierTypeEnum.DISSEMINATION.getValue()); + assertEquals(objectData.getFilename(), "Gallieni pour diffusion"); + } + + @Test + public void should_return_null_file_info_when_physical_master() + throws IOException, InvalidParseOperationException, VitamClientException { + // Given + ObjectData objectData = new ObjectData(); + RequestResponse<JsonNode> jsonNodeRequestResponse = buildGot(GOT_PHYSICAL); + ResultsDto resultsDto = buildResults(jsonNodeRequestResponse); + // When + when(unitService.findObjectMetadataById(any(), any())) + .thenReturn(jsonNodeRequestResponse); + when(archivesSearchService.findObjectById(any(), any())) + .thenReturn(ResponseEntity.of(Optional.of(resultsDto))); + String usage = archivesSearchService.getUsage(resultsDto, objectData); + + // Then + assertNull(usage); + } + + private ResultsDto buildResults(RequestResponse<JsonNode> jsonNodeRequestResponse) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + String re = StringUtils.chop(jsonNodeRequestResponse.toJsonNode().get("$results").toString().substring(1)); + return objectMapper.readValue(re, ResultsDto.class); + } + + private RequestResponse<JsonNode> buildGot(String filename) throws IOException, InvalidParseOperationException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + InputStream inputStream = ArchivesSearchServiceTest.class.getClassLoader() + .getResourceAsStream(filename); + return RequestResponseOK + .getFromJsonNode(objectMapper.readValue(ByteStreams.toByteArray(inputStream), JsonNode.class)); + } } diff --git a/ui/ui-archive-search/src/test/resources/data/vitam_got_binarymaster.json b/ui/ui-archive-search/src/test/resources/data/vitam_got_binarymaster.json new file mode 100644 index 000000000..19c7c3d10 --- /dev/null +++ b/ui/ui-archive-search/src/test/resources/data/vitam_got_binarymaster.json @@ -0,0 +1,76 @@ +{ + "httpCode": 200, + "$hits": { + "total": 1, + "offset": 0, + "limit": 0, + "size": 1 + }, + "$results": [ + { + "FileInfo": { + "Filename": "Pereire.txt", + "LastModified": "2017-04-04T08:07:27.825+02:00" + }, + "#id": "aebaaaaaaahl2zz5ab23malq4gw2cnqaaaba", + "#nbobjects": 1, + "#qualifiers": [ + { + "qualifier": "BinaryMaster", + "#nbc": 1, + "versions": [ + { + "DataObjectGroupId": "aebaaaaaaahl2zz5ab23malq4gw2cnqaaaba", + "DataObjectVersion": "BinaryMaster_1", + "FormatIdentification": { + "FormatLitteral": "Plain Text File", + "MimeType": "text/plain", + "FormatId": "x-fmt/111" + }, + "FileInfo": { + "Filename": "this_is_a_BinaryMaster.txt", + "LastModified": "2017-04-04T08:07:27.825+02:00" + }, + "Size": 6, + "Uri": "Content/ID35.txt", + "MessageDigest": "664ac614a819df2a97d2a5df57dcad91d6ec38b0fffc793e80c56b4553a14ac7a5f0bce3bb71af419b0bb8f151ad3d512867454eeb818e01818a31989c13319b", + "Algorithm": "SHA-512", + "#id": "aeaaaaaaaahl2zz5ab23malq4gw2cnqaaaaq", + "#opi": "aeeaaaaaachf2vysaau5malrcg7nsjqaaaaq", + "#storage": { + "strategyId": "default" + } + } + ] + } + ], + "#type": "", + "#tenant": 0, + "#unitups": [ + "aeaqaaaaaahl2zz5ab23malq4gw2eqqaaaba" + ], + "#allunitups": [ + ], + "#operations": [ + "aeeaaaaaachf2vysaau5malrcg7nsjqaaaaq" + ], + "#opi": "aeeaaaaaachf2vysaau5malrcg7nsjqaaaaq", + "#originating_agency": "RATP", + "#originating_agencies": [ + "RATP" + ], + "#storage": { + "strategyId": "default" + }, + "#version": 1 + } + ], + "$facetResults": [], + "$context": { + "$roots": [], + "$query": [], + "$filter": {}, + "$projection": {}, + "$facets": [] + } +} diff --git a/ui/ui-archive-search/src/test/resources/data/vitam_got_binarymaster_multiple_versions.json b/ui/ui-archive-search/src/test/resources/data/vitam_got_binarymaster_multiple_versions.json new file mode 100644 index 000000000..1fbc87df5 --- /dev/null +++ b/ui/ui-archive-search/src/test/resources/data/vitam_got_binarymaster_multiple_versions.json @@ -0,0 +1,102 @@ +{ + "httpCode": 200, + "$hits": { + "total": 1, + "offset": 0, + "limit": 0, + "size": 1 + }, + "$results": [ + { + "FileInfo": { + "Filename": "Pereire.txt", + "LastModified": "2017-04-04T08:07:27.825+02:00" + }, + "#id": "aebaaaaaaahl2zz5ab23malq4gw2cnqaaaba", + "#nbobjects": 1, + "#qualifiers": [ + { + "qualifier": "BinaryMaster", + "#nbc": 1, + "versions": [ + { + "DataObjectGroupId": "aebaaaaaaahl2zz5ab23malq4gw2cnqaaaba", + "DataObjectVersion": "BinaryMaster_1", + "FormatIdentification": { + "FormatLitteral": "Plain Text File", + "MimeType": "text/plain", + "FormatId": "x-fmt/111" + }, + "FileInfo": { + "Filename": "this_is_a_BinaryMaster.txt", + "LastModified": "2017-04-04T08:07:27.825+02:00" + }, + "Size": 6, + "Uri": "Content/ID35.txt", + "MessageDigest": "664ac614a819df2a97d2a5df57dcad91d6ec38b0fffc793e80c56b4553a14ac7a5f0bce3bb71af419b0bb8f151ad3d512867454eeb818e01818a31989c13319b", + "Algorithm": "SHA-512", + "#id": "aeaaaaaaaahl2zz5ab23malq4gw2cnqaaaaq", + "#opi": "aeeaaaaaachf2vysaau5malrcg7nsjqaaaaq", + "#storage": { + "strategyId": "default" + } + } + ] + }, + { + "qualifier": "BinaryMaster", + "#nbc": 2, + "versions": [ + { + "DataObjectGroupId": "aebaaaaaaahl2zz5ab23malq4gw2cnqaaaba", + "DataObjectVersion": "BinaryMaster_2", + "FormatIdentification": { + "FormatLitteral": "Plain Text File", + "MimeType": "text/plain", + "FormatId": "x-fmt/111" + }, + "FileInfo": { + "Filename": "this_is_a_BinaryMaster.txt", + "LastModified": "2017-04-04T08:07:27.825+02:00" + }, + "Size": 6, + "Uri": "Content/ID35.txt", + "MessageDigest": "664ac614a819df2a97d2a5df57dcad91d6ec38b0fffc793e80c56b4553a14ac7a5f0bce3bb71af419b0bb8f151ad3d512867454eeb818e01818a31989c13319b", + "Algorithm": "SHA-512", + "#id": "aeaaaaaaaahl2zz5ab23malq4gw2cnqaaaaq", + "#opi": "aeeaaaaaachf2vysaau5malrcg7nsjqaaaaq", + "#storage": { + "strategyId": "default" + } + } + ] + } + ], + "#type": "", + "#tenant": 0, + "#unitups": [ + ], + "#allunitups": [ + ], + "#operations": [ + ], + "#opi": "aeeaaaaaachf2vysaau5malrcg7nsjqaaaaq", + "#originating_agency": "RATP", + "#originating_agencies": [ + "RATP" + ], + "#storage": { + "strategyId": "default" + }, + "#version": 1 + } + ], + "$facetResults": [], + "$context": { + "$roots": [], + "$query": [], + "$filter": {}, + "$projection": {}, + "$facets": [] + } +} diff --git a/ui/ui-archive-search/src/test/resources/data/vitam_got_binarymaster_without_fileInfo.json b/ui/ui-archive-search/src/test/resources/data/vitam_got_binarymaster_without_fileInfo.json new file mode 100644 index 000000000..7afa48f75 --- /dev/null +++ b/ui/ui-archive-search/src/test/resources/data/vitam_got_binarymaster_without_fileInfo.json @@ -0,0 +1,72 @@ +{ + "httpCode": 200, + "$hits": { + "total": 1, + "offset": 0, + "limit": 0, + "size": 1 + }, + "$results": [ + { + "FileInfo": { + "Filename": "Pereire.txt", + "LastModified": "2017-04-04T08:07:27.825+02:00" + }, + "#id": "aebaaaaaaahl2zz5ab23malq4gw2cnqaaaba", + "#nbobjects": 1, + "#qualifiers": [ + { + "qualifier": "BinaryMaster", + "_nbc": 1, + "versions": [ + { + "_id": "aeaaaaaaaahly3l5ab7vwalzlvsew3aaaaaq", + "DataObjectGroupId": "aebaaaaaaehly3l5ab7vwalzlvsew5iaaaaq", + "DataObjectVersion": "BinaryMaster_1", + "FormatIdentification": { + "FormatLitteral": "Plain Text File", + "MimeType": "text/plain", + "FormatId": "x-fmt/111" + }, + "_opi": "aeeaaaaaaghly3l5aad6ialzlvsdclaaaaaq", + "Size": 6, + "Uri": "Content/ID13.txt", + "MessageDigest": "86c0bc701ef6b5dd21b080bc5bb2af38097baa6237275da83a52f092c9eae3e4e4b0247391620bd732fe824d18bd3bb6c37e62ec73a8cf3585c6a799399861b1", + "Algorithm": "SHA-512", + "_storage": { + "strategyId": "default" + } + } + ] + } + ], + "#type": "", + "#tenant": 0, + "#unitups": [ + "aeaqaaaaaahl2zz5ab23malq4gw2eqqaaaba" + ], + "#allunitups": [ + ], + "#operations": [ + "aeeaaaaaachf2vysaau5malrcg7nsjqaaaaq" + ], + "#opi": "aeeaaaaaachf2vysaau5malrcg7nsjqaaaaq", + "#originating_agency": "RATP", + "#originating_agencies": [ + "RATP" + ], + "#storage": { + "strategyId": "default" + }, + "#version": 1 + } + ], + "$facetResults": [], + "$context": { + "$roots": [], + "$query": [], + "$filter": {}, + "$projection": {}, + "$facets": [] + } +} diff --git a/ui/ui-archive-search/src/test/resources/data/vitam_got_dissemination.json b/ui/ui-archive-search/src/test/resources/data/vitam_got_dissemination.json new file mode 100644 index 000000000..851682497 --- /dev/null +++ b/ui/ui-archive-search/src/test/resources/data/vitam_got_dissemination.json @@ -0,0 +1,76 @@ +{ + "httpCode": 200, + "$hits": { + "total": 1, + "offset": 0, + "limit": 0, + "size": 1 + }, + "$results": [ + { + "FileInfo": { + "Filename": "Pereire.txt", + "LastModified": "2017-04-04T08:07:27.825+02:00" + }, + "#id": "aebaaaaaaahl2zz5ab23malq4gw2cnqaaaba", + "#nbobjects": 1, + "#qualifiers": [ + { + "qualifier": "Dissemination", + "_nbc": 1, + "versions": [ + { + "_id": "aeaaaaaaaahlju6xaayh6alycfih5ziaaaba", + "DataObjectGroupId": "aebaaaaaaahlju6xaayh6alycfih5ziaaaaq", + "DataObjectVersion": "Dissemination_1", + "FormatIdentification": { + "FormatLitteral": "Portable Network Graphics", + "MimeType": "image/png", + "FormatId": "fmt/12" + }, + "FileInfo": { + "Filename": "Gallieni pour diffusion", + "LastModified": "2016-06-03T15:28:00.000+02:00" + }, + "_opi": "aeeaaaaaachlju6xabk74alycfihj7qaaaaq", + "Size": 57850, + "Uri": "Content/0f1ddc38a1eb38d401e394c057db3f2ebc646fd1cd0c06e7dc680c19d89c05f74815cbf27399fafe111192bcb4c0e246186cc8cc430c6ff7fe52a7fdbd2110f7.png", + "MessageDigest": "0f1ddc38a1eb38d401e394c057db3f2ebc646fd1cd0c06e7dc680c19d89c05f74815cbf27399fafe111192bcb4c0e246186cc8cc430c6ff7fe52a7fdbd2110f7", + "Algorithm": "SHA-512", + "_storage": { + "strategyId": "default" + } + } + ] + } + ], + "#type": "", + "#tenant": 0, + "#unitups": [ + "aeaqaaaaaahl2zz5ab23malq4gw2eqqaaaba" + ], + "#allunitups": [ + ], + "#operations": [ + "aeeaaaaaachf2vysaau5malrcg7nsjqaaaaq" + ], + "#opi": "aeeaaaaaachf2vysaau5malrcg7nsjqaaaaq", + "#originating_agency": "RATP", + "#originating_agencies": [ + "RATP" + ], + "#storage": { + "strategyId": "default" + }, + "#version": 1 + } + ], + "$facetResults": [], + "$context": { + "$roots": [], + "$query": [], + "$filter": {}, + "$projection": {}, + "$facets": [] + } +} diff --git a/ui/ui-archive-search/src/test/resources/data/vitam_got_physical.json b/ui/ui-archive-search/src/test/resources/data/vitam_got_physical.json new file mode 100644 index 000000000..579c39dbe --- /dev/null +++ b/ui/ui-archive-search/src/test/resources/data/vitam_got_physical.json @@ -0,0 +1,84 @@ +{ + "httpCode": 200, + "$hits": { + "total": 1, + "offset": 0, + "limit": 0, + "size": 1 + }, + "$results": [ + { + "FileInfo": { + "Filename": "Pereire.txt", + "LastModified": "2017-04-04T08:07:27.825+02:00" + }, + "#id": "aebaaaaaaahl2zz5ab23malq4gw2cnqaaaba", + "#nbobjects": 1, + "#qualifiers": [ + { + "qualifier": "PhysicalMaster", + "_nbc": 1, + "versions": [ + { + "_id": "aeaaaaaaaahlju6xaayh6alycfwfrcyaaaba", + "DataObjectGroupId": "aebaaaaaaahlju6xaayh6alycfwfqwyaaaaq", + "DataObjectVersion": "PhysicalMaster_1", + "PhysicalId": "1 Num 1/191-2", + "PhysicalDimensions": { + "Height": { + "unit": "centimetre", + "dValue": 10.5 + }, + "Length": { + "unit": "centimetre", + "dValue": 14.8 + }, + "Thickness": { + "unit": "micrometre", + "dValue": 350 + }, + "Weight": { + "unit": "gram", + "dValue": 3 + } + }, + "Extent": "1 carte imprimée", + "Dimensions": "10,5cm x 14,8cm", + "Color": "Noir et blanc", + "Framing": "Paysage", + "Technique": "Phototypie", + "_opi": "aeeaaaaaachlju6xabk74alycfwe5haaaaaq" + } + ] + } + ], + "#type": "", + "#tenant": 0, + "#unitups": [ + "aeaqaaaaaahl2zz5ab23malq4gw2eqqaaaba" + ], + "#allunitups": [ + ], + "#operations": [ + "aeeaaaaaachf2vysaau5malrcg7nsjqaaaaq" + ], + "#opi": "aeeaaaaaachf2vysaau5malrcg7nsjqaaaaq", + "#originating_agency": "RATP", + "#originating_agencies": [ + "RATP" + ], + "#storage": { + "strategyId": "default" + }, + "#version": 1 + } + ], + "$facetResults": [], + "$context": { + "$roots": [], + "$query": [], + "$filter": {}, + "$projection": {}, + "$facets": [] + } +} diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-preview/archive-preview.component.ts b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-preview/archive-preview.component.ts index e099d1535..540ddd126 100644 --- a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-preview/archive-preview.component.ts +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-preview/archive-preview.component.ts @@ -35,11 +35,11 @@ export class ArchivePreviewComponent implements OnInit { } - onDownloadObjectFromUnit(archiveUnit : Unit){ + onDownloadObjectFromUnit(archiveUnit: Unit) { let headers = new HttpHeaders().append('Content-Type', 'application/json'); headers = headers.append('X-Access-Contract-Id', this.accessContract); - return this.archiveService.downloadObjectFromUnit(archiveUnit['#id'], archiveUnit.Title, headers); + return this.archiveService.downloadObjectFromUnit(archiveUnit['#id'], archiveUnit?.Title , archiveUnit?.Title_, headers); } emitClose() { diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/archive.service.ts b/ui/ui-frontend/projects/archive-search/src/app/archive/archive.service.ts index f2a1d76fe..82f5d8dac 100644 --- a/ui/ui-frontend/projects/archive-search/src/app/archive/archive.service.ts +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/archive.service.ts @@ -187,29 +187,49 @@ export class ArchiveService extends SearchService<any> { return pagedResult; } - downloadObjectFromUnit(id : string , name : string, headers?: HttpHeaders) { + downloadObjectFromUnit(id: string, title?: string, title_?: any, headers?: HttpHeaders) { return this.archiveApiService.downloadObjectFromUnit(id, headers).subscribe( - file => { + response => { + let filename = null; + if (response.headers.get('content-disposition').includes('filename')) { + filename = response.headers.get('content-disposition').split('=')[1]; + } else { + filename = this.normalizeTitle(title ? title : ((title_) ? (title_.fr ? title_.fr : title_.en) : title_.en)); + } const element = document.createElement('a'); - element.href = window.URL.createObjectURL(file); - element.download =name; + console.log('headers filename = ', filename); + element.href = window.URL.createObjectURL(response.body); + element.download = filename; + console.log('response = ', response); element.style.visibility = 'hidden'; document.body.appendChild(element); element.click(); document.body.removeChild(element); }, errors => { - console.log('Error message : ',errors); + console.log('Error message : ', errors); } - ); } + ); + } + + normalizeTitle(title: string): string { + title = title.replace(/[&\/\\|.'":*?<> ]/g, ''); + return title.substring(0, 218); + } - findArchiveUnit(id : string, headers?: HttpHeaders) { + findArchiveUnit(id: string, headers?: HttpHeaders) { return this.archiveApiService.findArchiveUnit(id, headers); } -} + + getObjectById(id: string,headers?: HttpHeaders) { + return this.archiveApiService.getObjectById(id,headers); + } + + } + function idExists(units: Unit[], id: string): boolean { return !!units.find((unit) => unit['#id'] === id); diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/models/object-data.ts b/ui/ui-frontend/projects/archive-search/src/app/archive/models/object-data.ts new file mode 100644 index 000000000..dafab0626 --- /dev/null +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/models/object-data.ts @@ -0,0 +1,42 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ + +export interface ObjectData { + resource: Blob; + filename: string; + mediaType: any; +} diff --git a/ui/ui-frontend/projects/archive-search/src/app/core/api/archive-api.service.ts b/ui/ui-frontend/projects/archive-search/src/app/core/api/archive-api.service.ts index 5e0d97476..50e8a56d0 100644 --- a/ui/ui-frontend/projects/archive-search/src/app/core/api/archive-api.service.ts +++ b/ui/ui-frontend/projects/archive-search/src/app/core/api/archive-api.service.ts @@ -35,7 +35,7 @@ * knowledge of the CeCILL-C license and that you accept its terms. */ import { Inject, Injectable } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http'; import { BASE_URL, BaseHttpClient, PageRequest, PaginatedResponse } from 'ui-frontend-common'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @@ -44,7 +44,6 @@ import { SearchResponse } from '../../archive/models/search-response.interface'; import { SearchCriteriaHistory } from '../../archive/models/search-criteria-history.interface'; - @Injectable({ providedIn: 'root' }) @@ -79,7 +78,7 @@ export class ArchiveApiService extends BaseHttpClient<any> { return this.http.post<SearchResponse>( `${this.apiUrl}/search`, criteriaDto, {headers}); } - exportCsvSearchArchiveUnitsByCriteria(criteriaDto: SearchCriteriaDto, headers?: HttpHeaders): Observable<Blob> { + exportCsvSearchArchiveUnitsByCriteria(criteriaDto: SearchCriteriaDto, headers?: HttpHeaders): Observable<Blob> { return this.http.post(`${this.apiUrl}/export-csv-search`, criteriaDto, { responseType: 'blob', headers: headers @@ -92,11 +91,12 @@ export class ArchiveApiService extends BaseHttpClient<any> { return this.http.get<any>(`${this.apiUrl}/accesscontracts`, { params, headers }); } - downloadObjectFromUnit(id : string, headers?: HttpHeaders) : Observable<Blob> { - return this.http.get(`${this.apiUrl}/downloadobjectfromunit/${id}`,{headers: headers, responseType: 'blob'}); + downloadObjectFromUnit(id: string, headers?: HttpHeaders): Observable<HttpResponse<Blob>> { + // tslint:disable-next-line:max-line-length + return this.http.get(`${this.apiUrl}/downloadobjectfromunit/${id}`,{headers: headers, observe: 'response', responseType: 'blob' }); } - findArchiveUnit(id: string, headers?: HttpHeaders) :Observable<any> { + findArchiveUnit(id: string, headers?: HttpHeaders): Observable<any> { return this.http.get(`${this.apiUrl}/archiveunit/${id}`, { headers: headers, responseType: 'text' }); } @@ -115,4 +115,7 @@ export class ArchiveApiService extends BaseHttpClient<any> { updateSearchCriteriaHistory(searchCriteriaHistory: SearchCriteriaHistory): Observable<SearchCriteriaHistory> { return this.http.put<SearchCriteriaHistory>(`${this.apiUrl}/searchcriteriahistory/${searchCriteriaHistory.id}`, searchCriteriaHistory); } + getObjectById(id: string,headers?: HttpHeaders): Observable<any> { + return this.http.get(`${this.apiUrl}/object/${id}`,{headers: headers,responseType: 'text'}); + } } -- GitLab