diff --git a/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/common/ArchiveSearchConsts.java b/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/common/ArchiveSearchConsts.java index 9aa9b816fa4409d731f005a4fe67a55b73b8af63..d8639e7e6e21e8ad1ef96b19e9590698903ec24f 100644 --- a/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/common/ArchiveSearchConsts.java +++ b/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/common/ArchiveSearchConsts.java @@ -167,6 +167,8 @@ public class ArchiveSearchConsts { public static final String UNITS_UPS = "#allunitups"; public static final String END_DATE = "EndDate"; public static final String TITLE_OR_DESCRIPTION = "TITLE_OR_DESCRIPTION"; + public static final String ELIMINATION_TECHNICAL_ID = "ELIMINATION_TECHNICAL_ID"; + public static final String ELIMINATION_GUID = "#elimination.OperationId"; /* Query fields */ public static final String ID = "#id"; diff --git a/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dsl/VitamQueryHelper.java b/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dsl/VitamQueryHelper.java index c8268ce2a0799a409cfda9c4ab9f5919b5ee99fa..8b77882d228bdbad561e190d38f8cc16203a8919 100644 --- a/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dsl/VitamQueryHelper.java +++ b/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dsl/VitamQueryHelper.java @@ -68,20 +68,34 @@ public class VitamQueryHelper { if (searchKey == null || "".equals(searchKey.trim())) { throw new InvalidCreateOperationException("searchKey is empty or null "); } - if (CollectionUtils.isEmpty(searchValues)) { - //the case of empty list - query.add(buildSubQueryByOperator(searchKey, null, operator)); - } else if (searchValues.size() > 1) { - BooleanQuery subQueryOr = or(); - //The case of multiple values , => Or operator - for (String value : searchValues) { - subQueryOr.add(buildSubQueryByOperator(searchKey, value, operator)); + if (CollectionUtils.isEmpty(searchValues)) { + //the case of empty list + query.add(buildSubQueryByOperator(searchKey, null, operator)); + } else if (searchValues.size() > 1) { + BooleanQuery subQueryOr = or(); + BooleanQuery subQueryAnd = and(); + //The case of multiple values + if(operator == ArchiveSearchConsts.CriteriaOperators.NOT_EQ) { + for (String value : searchValues) { + subQueryAnd.add(buildSubQueryByOperator(searchKey, value, operator)); + + } + query.add(subQueryAnd); + } + else { + for (String value : searchValues) { + subQueryOr.add(buildSubQueryByOperator(searchKey, value, operator)); + } + query.add(subQueryOr); + } + + } else if (searchValues.size() == 1) { + //the case of one value + query.add(buildSubQueryByOperator(searchKey, searchValues.stream().findAny().get(), operator)); } - query.add(subQueryOr); - } else if (searchValues.size() == 1) { - //the case of one value - query.add(buildSubQueryByOperator(searchKey, searchValues.stream().findAny().get(), operator)); - } + + + } public static void addDatesCriteriaToQuery(BooleanQuery mainQuery, final String criteria, diff --git a/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dto/ExportDipCriteriaDto.java b/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dto/ExportDipCriteriaDto.java new file mode 100644 index 0000000000000000000000000000000000000000..0badfde3efd2827886a82e146eb7308b8d914009 --- /dev/null +++ b/api/api-archive-search/archive-search-commons/src/main/java/fr/gouv/vitamui/archives/search/common/dto/ExportDipCriteriaDto.java @@ -0,0 +1,60 @@ +/** + * 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.archives.search.common.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import fr.gouv.vitam.common.model.export.dip.DipRequestParameters; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.io.Serializable; +import java.util.Set; + +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +@Setter +@Getter +public class ExportDipCriteriaDto implements Serializable { + + private DipRequestParameters dipRequestParameters; + private SearchCriteriaDto exportDIPSearchCriteria; + private Set<String> dataObjectVersions ; + private boolean lifeCycleLogs; + +} 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 02e7a2f2a49730178ce5a8a364b4ba4ea02edfe0..be9b45b53a0559d03a01466bb96dcd62e94f7945 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 @@ -37,4 +37,7 @@ public class RestApi { public static final String ARCHIVE_UNIT_INFO = "/archiveunit"; public static final String SEARCH_CRITERIA_HISTORY = "/searchcriteriahistory"; public static final String OBJECTGROUP = "/object"; + public static final String EXPORT_DIP = "/export-dip"; + public static final String ELIMINATION_ANALYSIS = "/elimination/analysis"; + public static final String ELIMINATION_ACTION = "/elimination/action"; } 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 ae43976e671324df7940b0b9b896c1d1f53e6202..7de565ab8895a201f38d59c59c7405ecb5383ba2 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 @@ -27,7 +27,9 @@ package fr.gouv.vitamui.archives.search.external.client; +import com.fasterxml.jackson.databind.JsonNode; import fr.gouv.vitamui.archives.search.common.dto.ArchiveUnitsDto; +import fr.gouv.vitamui.archives.search.common.dto.ExportDipCriteriaDto; import fr.gouv.vitamui.archives.search.common.dto.SearchCriteriaDto; import fr.gouv.vitamui.archives.search.common.rest.RestApi; import fr.gouv.vitamui.commons.api.CommonConstants; @@ -38,14 +40,12 @@ 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; @@ -157,4 +157,22 @@ public class ArchiveSearchExternalRestClient request, Resource.class); return response; } + + public ResponseEntity<String> exportDIPCriteria(ExportDipCriteriaDto exportDipCriteriaDto, ExternalHttpContext context) { + LOGGER.debug("Calling export DIP by criteria"); + MultiValueMap<String, String> headers = buildSearchHeaders(context); + final HttpEntity<ExportDipCriteriaDto> request = new HttpEntity<>(exportDipCriteriaDto, headers); + final ResponseEntity<String> response = + restTemplate.exchange(getUrl() + RestApi.EXPORT_DIP, HttpMethod.POST, + request, String.class); + return response; + } + + public ResponseEntity<JsonNode> startEliminationAnalysis(ExternalHttpContext context, SearchCriteriaDto query) { + LOGGER.debug("Calling elimination analysis by criteria"); + MultiValueMap<String, String> headers = buildSearchHeaders(context); + final HttpEntity<SearchCriteriaDto> request = new HttpEntity<>(query, headers); + return restTemplate.exchange(getUrl() + RestApi.ELIMINATION_ANALYSIS, HttpMethod.POST, + request, JsonNode.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 a7173a86d0a278c628e9ef4dfb841a4ed8648687..d1962c2e6082af5712c63d30d5294e01b7b39c2a 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 @@ -28,7 +28,9 @@ package fr.gouv.vitamui.archives.search.external.server.rest; +import com.fasterxml.jackson.databind.JsonNode; import fr.gouv.vitamui.archives.search.common.dto.ArchiveUnitsDto; +import fr.gouv.vitamui.archives.search.common.dto.ExportDipCriteriaDto; import fr.gouv.vitamui.archives.search.common.dto.SearchCriteriaDto; import fr.gouv.vitamui.archives.search.common.rest.RestApi; import fr.gouv.vitamui.archives.search.external.server.service.ArchivesSearchExternalService; @@ -123,4 +125,21 @@ public class ArchivesSearchExternalController { return archivesSearchExternalService.exportCsvArchiveUnitsByCriteria(query); } + @PostMapping(RestApi.EXPORT_DIP) + @Secured(ServicesData.ROLE_EXPORT_DIP) + public String exportDIPByCriteria(final @RequestBody ExportDipCriteriaDto exportDipCriteriaDto) { + LOGGER.info("Calling export DIP By Criteria {} ", exportDipCriteriaDto); + ParameterChecker.checkParameter("The query is a mandatory parameter: ", exportDipCriteriaDto); + SanityChecker.sanitizeCriteria(exportDipCriteriaDto); + return archivesSearchExternalService.exportDIPByCriteria(exportDipCriteriaDto); + } + + @PostMapping(RestApi.ELIMINATION_ANALYSIS) + @Secured(ServicesData.ROLE_ELIMINATION_ANALYSIS) + public ResponseEntity<JsonNode> startEliminationAnalysis(final @RequestBody SearchCriteriaDto query) { + LOGGER.info("Calling elimination analysis by criteria {} ", query); + ParameterChecker.checkParameter("The query is a mandatory parameter: ", query); + SanityChecker.sanitizeCriteria(query); + return archivesSearchExternalService.startEliminationAnalysis(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 ede87fb782f19fd859d15784e66f2ac962ed3796..c2a4e75cf8d694e76f664d5606fbd99eb93a8c7a 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 @@ -27,9 +27,11 @@ package fr.gouv.vitamui.archives.search.external.server.service; +import com.fasterxml.jackson.databind.JsonNode; import fr.gouv.archive.internal.client.ArchiveInternalRestClient; import fr.gouv.archive.internal.client.ArchiveInternalWebClient; import fr.gouv.vitamui.archives.search.common.dto.ArchiveUnitsDto; +import fr.gouv.vitamui.archives.search.common.dto.ExportDipCriteriaDto; import fr.gouv.vitamui.archives.search.common.dto.SearchCriteriaDto; import fr.gouv.vitamui.commons.vitam.api.dto.ResultsDto; import fr.gouv.vitamui.commons.vitam.api.dto.VitamUISearchResponseDto; @@ -94,4 +96,12 @@ public class ArchivesSearchExternalService extends AbstractResourceClientService public Resource exportCsvArchiveUnitsByCriteria(final SearchCriteriaDto query) { return archiveInternalRestClient.exportCsvArchiveUnitsByCriteria(query, getInternalHttpContext()); } + + public String exportDIPByCriteria(final ExportDipCriteriaDto exportDipCriteriaDto) { + return archiveInternalRestClient.exportDIPByCriteria(exportDipCriteriaDto, getInternalHttpContext()); + } + + public ResponseEntity<JsonNode> startEliminationAnalysis(final SearchCriteriaDto query) { + return archiveInternalRestClient.startEliminationAnalysis(getInternalHttpContext(), 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 88ca8035f87c8969972deaa5fa4631a3d5b67bcd..4693b8c9d518e34b20ec1f08b2f373012961bb42 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 @@ -28,6 +28,7 @@ package fr.gouv.archive.internal.client; import com.fasterxml.jackson.databind.JsonNode; import fr.gouv.vitamui.archives.search.common.dto.ArchiveUnitsDto; +import fr.gouv.vitamui.archives.search.common.dto.ExportDipCriteriaDto; import fr.gouv.vitamui.archives.search.common.dto.SearchCriteriaDto; import fr.gouv.vitamui.archives.search.common.rest.RestApi; import fr.gouv.vitamui.commons.api.CommonConstants; @@ -158,4 +159,29 @@ public class ArchiveInternalRestClient return response.getBody(); } + + public String exportDIPByCriteria(final ExportDipCriteriaDto exportDipCriteriaDto, + final InternalHttpContext context) { + LOGGER.info("Calling exportDIPByCriteria with query {} ", exportDipCriteriaDto); + MultiValueMap<String, String> headers = buildSearchHeaders(context); + final HttpEntity<ExportDipCriteriaDto> request = new HttpEntity<>(exportDipCriteriaDto, headers); + final ResponseEntity<String> response = + restTemplate.exchange(getUrl() + RestApi.EXPORT_DIP, HttpMethod.POST, + request, String.class); + checkResponse(response); + return response.getBody(); + + } + + public ResponseEntity<JsonNode> startEliminationAnalysis(final InternalHttpContext context, final SearchCriteriaDto query) { + LOGGER.info("Calling elimination analysis with query {} ", query); + MultiValueMap<String, String> headers = buildSearchHeaders(context); + final HttpEntity<SearchCriteriaDto> request = new HttpEntity<>(query, headers); + final ResponseEntity<JsonNode> response = + restTemplate.exchange(getUrl() + RestApi.ELIMINATION_ANALYSIS, HttpMethod.POST, + request, JsonNode.class); + checkResponse(response); + return response; + + } } diff --git a/api/api-archive-search/archive-search-internal/pom.xml b/api/api-archive-search/archive-search-internal/pom.xml index 06ac65d2b80f2541e28ece3b6bc92eeb2699be56..8ba8e6fc65e1d71b65c1675176aa48d3f0e7a120 100644 --- a/api/api-archive-search/archive-search-internal/pom.xml +++ b/api/api-archive-search/archive-search-internal/pom.xml @@ -204,7 +204,9 @@ <executable>true</executable> <attach>false</attach> <!-- Need to use the original jar for integration-tests --> <mainClass>fr.gouv.vitamui.archive.internal.server.ApiArchiveInternalServerApplication</mainClass> - <jvmArguments>-Xmx512m -Dvitam.config.folder=${basedir}/src/main/config/dev-vitam</jvmArguments> + <jvmArguments> + -Xmx512m -Dvitam.config.folder=${basedir}/src/main/config/dev-vitam + </jvmArguments> <arguments> <!-- use src/main/config/application-dev.yml when using mvn spring-boot:run --> diff --git a/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/config/ArchiveSearchInternalServerConfig.java b/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/config/ArchiveSearchInternalServerConfig.java index 4e06129ca933da71e03d4cb29cde99ce201c885c..894b814ae6ade6de2d907681be8665c1ca864b95 100644 --- a/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/config/ArchiveSearchInternalServerConfig.java +++ b/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/config/ArchiveSearchInternalServerConfig.java @@ -27,6 +27,7 @@ package fr.gouv.vitamui.archive.internal.server.config; import fr.gouv.vitam.access.external.client.AccessExternalClient; +import fr.gouv.vitam.access.external.client.v2.AccessExternalClientV2; import fr.gouv.vitamui.archive.internal.server.searchcriteria.converter.SearchCriteriaHistoryConverter; import fr.gouv.vitamui.archive.internal.server.searchcriteria.dao.SearchCriteriaHistoryRepository; import fr.gouv.vitamui.archive.internal.server.searchcriteria.service.SearchCriteriaHistoryInternalService; @@ -35,6 +36,7 @@ import fr.gouv.vitamui.commons.mongo.dao.CustomSequenceRepository; import fr.gouv.vitamui.commons.rest.RestExceptionHandler; import fr.gouv.vitamui.commons.rest.client.configuration.RestClientConfiguration; import fr.gouv.vitamui.commons.rest.configuration.SwaggerConfiguration; +import fr.gouv.vitamui.commons.vitam.api.access.ExportDipV2Service; import fr.gouv.vitamui.commons.vitam.api.access.UnitService; import fr.gouv.vitamui.commons.vitam.api.config.VitamAccessConfig; import fr.gouv.vitamui.commons.vitam.api.config.VitamAdministrationConfig; @@ -96,6 +98,11 @@ public class ArchiveSearchInternalServerConfig extends AbstractContextConfigurat return new UnitService(client); } + @Bean + public ExportDipV2Service exportDipV2Service(final AccessExternalClientV2 accessExternalClientV2) { + return new ExportDipV2Service(accessExternalClientV2); + } + @Bean public SearchCriteriaHistoryInternalService searchCriteriaHistoryInternalService( final CustomSequenceRepository sequenceRepository, 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 c7869079111f93437be5d8513663a8ec5e6030d9..e166af28889112747d6ec20add8b6c4133da227f 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 @@ -36,6 +36,7 @@ import fr.gouv.vitam.common.exception.InvalidParseOperationException; import fr.gouv.vitam.common.exception.VitamClientException; import fr.gouv.vitamui.archive.internal.server.service.ArchiveSearchInternalService; import fr.gouv.vitamui.archives.search.common.dto.ArchiveUnitsDto; +import fr.gouv.vitamui.archives.search.common.dto.ExportDipCriteriaDto; import fr.gouv.vitamui.archives.search.common.dto.SearchCriteriaDto; import fr.gouv.vitamui.archives.search.common.rest.RestApi; import fr.gouv.vitamui.common.security.SanityChecker; @@ -206,4 +207,36 @@ public class ArchiveSearchInternalController { return new ResponseEntity<>(exportedResult, HttpStatus.OK); } + @PostMapping(RestApi.EXPORT_DIP) + public ResponseEntity<String> exportDIPByCriteria( + @RequestHeader(value = CommonConstants.X_TENANT_ID_HEADER) final Integer tenantId, + @RequestHeader(value = CommonConstants.X_ACCESS_CONTRACT_ID_HEADER) final String accessContractId, + @RequestBody final ExportDipCriteriaDto exportDipCriteriaDto) + throws VitamClientException { + LOGGER.info("Export DIP by criteria {}", exportDipCriteriaDto); + SanityChecker.sanitizeCriteria(exportDipCriteriaDto); + ParameterChecker + .checkParameter("The tenant Id, the accessContract Id and the SearchCriteria are mandatory parameters: ", + tenantId, accessContractId, exportDipCriteriaDto); + final VitamContext vitamContext = securityService.buildVitamContext(tenantId, accessContractId); + String result = archiveInternalService.requestToExportDIP(exportDipCriteriaDto, vitamContext); + return new ResponseEntity<>(result, HttpStatus.OK); + } + + @PostMapping(RestApi.ELIMINATION_ANALYSIS) + public JsonNode startEliminationAnalysis( + @RequestHeader(value = CommonConstants.X_TENANT_ID_HEADER) final Integer tenantId, + @RequestHeader(value = CommonConstants.X_ACCESS_CONTRACT_ID_HEADER) final String accessContractId, + @RequestBody final SearchCriteriaDto searchQuery) + throws VitamClientException { + LOGGER.info("Calling elimination analysis by criteria {} ", searchQuery); + SanityChecker.sanitizeCriteria(searchQuery); + ParameterChecker + .checkParameter("The tenant Id, the accessContract Id and the SearchCriteria are mandatory parameters: ", + tenantId, accessContractId, searchQuery); + final VitamContext vitamContext = securityService.buildVitamContext(tenantId, accessContractId); + JsonNode jsonNode = archiveInternalService.startEliminationAnalysis(searchQuery, vitamContext); + return jsonNode; + } + } 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 b292f6e1ef182da397d2638dd50cb90f84f15187..41eb5fed119d5e53fb0c92c0e0a25c1c5ec8b2d0 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 @@ -29,6 +29,7 @@ package fr.gouv.vitamui.archive.internal.server.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.opencsv.CSVWriter; import com.opencsv.CSVWriterBuilder; import com.opencsv.ICSVWriter; @@ -36,17 +37,24 @@ import fr.gouv.vitam.common.LocalDateUtil; import fr.gouv.vitam.common.client.VitamContext; import fr.gouv.vitam.common.database.builder.facet.FacetHelper; import fr.gouv.vitam.common.database.builder.query.BooleanQuery; +import fr.gouv.vitam.common.database.builder.request.configuration.BuilderToken; import fr.gouv.vitam.common.database.builder.request.exception.InvalidCreateOperationException; import fr.gouv.vitam.common.database.builder.request.multiple.SelectMultiQuery; import fr.gouv.vitam.common.database.facet.model.FacetOrder; import fr.gouv.vitam.common.exception.InvalidParseOperationException; import fr.gouv.vitam.common.exception.VitamClientException; +import fr.gouv.vitam.common.json.JsonHandler; import fr.gouv.vitam.common.model.RequestResponse; +import fr.gouv.vitam.common.model.dip.DataObjectVersions; +import fr.gouv.vitam.common.model.export.dip.DipExportType; +import fr.gouv.vitam.common.model.export.dip.DipRequest; +import fr.gouv.vitam.common.model.elimination.EliminationRequestBody; import fr.gouv.vitamui.archives.search.common.common.ArchiveSearchConsts; import fr.gouv.vitamui.archives.search.common.dto.ArchiveUnit; import fr.gouv.vitamui.archives.search.common.dto.ArchiveUnitCsv; import fr.gouv.vitamui.archives.search.common.dto.ArchiveUnitsDto; import fr.gouv.vitamui.archives.search.common.dto.CriteriaValue; +import fr.gouv.vitamui.archives.search.common.dto.ExportDipCriteriaDto; import fr.gouv.vitamui.archives.search.common.dto.ExportSearchResultParam; import fr.gouv.vitamui.archives.search.common.dto.SearchCriteriaDto; import fr.gouv.vitamui.archives.search.common.dto.SearchCriteriaEltDto; @@ -55,9 +63,12 @@ import fr.gouv.vitamui.commons.api.domain.AgencyModelDto; import fr.gouv.vitamui.commons.api.domain.DirectionDto; import fr.gouv.vitamui.commons.api.exception.BadRequestException; import fr.gouv.vitamui.commons.api.exception.InternalServerException; +import fr.gouv.vitamui.commons.api.exception.PreconditionFailedException; 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.EliminationService; +import fr.gouv.vitamui.commons.vitam.api.access.ExportDipV2Service; 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; @@ -79,6 +90,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Map; @@ -98,9 +110,9 @@ public class ArchiveSearchInternalService { VitamUILoggerFactory.getInstance(ArchiveSearchInternalService.class); private static final String INGEST_ARCHIVE_TYPE = "INGEST"; private static final String ARCHIVE_UNIT_DETAILS = "$results"; - private static final String ARCHIVE_UNIT_USAGE = "qualifier"; - private static final String ARCHIVE_UNIT_VERSION = "DataObjectVersion"; private static final Integer EXPORT_ARCHIVE_UNITS_MAX_ELEMENTS = 10000; + private static final Integer EXPORT_DIP_MAX_ELEMENTS = 10000; + private static final Integer ELIMINATION_ANALYSIS_THRESHOLD = 10000; public static final String SEMI_COLON = ";"; public static final String COMMA = ","; public static final String DOUBLE_QUOTE = "\""; @@ -108,15 +120,18 @@ public class ArchiveSearchInternalService { public static final String NEW_LINE = "\n"; public static final String NEW_TAB = "\t"; public static final String NEW_LINE_1 = "\r\n"; + public static final String OPERATION_IDENTIFIER = "itemId"; public static final String SPACE = " "; private final ObjectMapper objectMapper; private final UnitService unitService; + private final EliminationService eliminationService; private final ArchiveSearchAgenciesInternalService archiveSearchAgenciesInternalService; private final ArchiveSearchRulesInternalService archiveSearchRulesInternalService; private final ArchivesSearchAppraisalQueryBuilderService archivesSearchAppraisalQueryBuilderService; private final ArchivesSearchFieldsQueryBuilderService archivesSearchFieldsQueryBuilderService; + private final ExportDipV2Service exportDipV2Service; @Autowired @@ -124,7 +139,9 @@ public class ArchiveSearchInternalService { final ArchiveSearchAgenciesInternalService archiveSearchAgenciesInternalService, final ArchiveSearchRulesInternalService archiveSearchRulesInternalService, final ArchivesSearchFieldsQueryBuilderService archivesSearchFieldsQueryBuilderService, - final ArchivesSearchAppraisalQueryBuilderService archivesSearchAppraisalQueryBuilderService + final ExportDipV2Service exportDipV2Service, + final ArchivesSearchAppraisalQueryBuilderService archivesSearchAppraisalQueryBuilderService, + final EliminationService eliminationService ) { this.unitService = unitService; @@ -133,6 +150,8 @@ public class ArchiveSearchInternalService { this.archiveSearchRulesInternalService = archiveSearchRulesInternalService; this.archivesSearchFieldsQueryBuilderService = archivesSearchFieldsQueryBuilderService; this.archivesSearchAppraisalQueryBuilderService = archivesSearchAppraisalQueryBuilderService; + this.exportDipV2Service = exportDipV2Service; + this.eliminationService = eliminationService; } public ArchiveUnitsDto searchArchiveUnitsByCriteria(final SearchCriteriaDto searchQuery, @@ -509,4 +528,81 @@ public class ArchiveSearchInternalService { LOGGER.info("Final query: {}", select.getFinalSelect().toPrettyString()); return select.getFinalSelect(); } + + public String requestToExportDIP(final ExportDipCriteriaDto exportDipCriteriaDto, + final VitamContext vitamContext) + throws VitamClientException { + + LOGGER.debug("Export DIP by criteria {} ", exportDipCriteriaDto); + exportDipCriteriaDto.getExportDIPSearchCriteria().setPageNumber(0); + exportDipCriteriaDto.getExportDIPSearchCriteria().setSize(EXPORT_DIP_MAX_ELEMENTS); + archiveSearchAgenciesInternalService.mapAgenciesNameToCodes(exportDipCriteriaDto.getExportDIPSearchCriteria(), vitamContext); + archiveSearchRulesInternalService.mapAppraisalRulesTitlesToCodes(exportDipCriteriaDto.getExportDIPSearchCriteria(), vitamContext); + JsonNode dslQuery = mapRequestToDslQuery(exportDipCriteriaDto.getExportDIPSearchCriteria()); + + DataObjectVersions dataObjectVersionToExport = new DataObjectVersions(); + dataObjectVersionToExport.setDataObjectVersions(exportDipCriteriaDto.getDataObjectVersions()); + DipRequest dipRequest = prepareDipRequestBody(exportDipCriteriaDto, dslQuery); + + JsonNode response = exportDIP(vitamContext,dipRequest); + return response.findValue(OPERATION_IDENTIFIER).textValue(); + } + + public DipRequest prepareDipRequestBody(final ExportDipCriteriaDto exportDipCriteriaDto, JsonNode dslQuery) { + DipRequest dipRequest = new DipRequest(); + + if(exportDipCriteriaDto != null) { + DataObjectVersions dataObjectVersionToExport = new DataObjectVersions(); + dataObjectVersionToExport.setDataObjectVersions(exportDipCriteriaDto.getDataObjectVersions()); + dipRequest.setExportWithLogBookLFC(exportDipCriteriaDto.isLifeCycleLogs()); + dipRequest.setDslRequest(dslQuery); + dipRequest.setDipExportType(DipExportType.FULL); + dipRequest.setDataObjectVersionToExport(dataObjectVersionToExport); + dipRequest.setDipRequestParameters(exportDipCriteriaDto.getDipRequestParameters()); + } + return dipRequest; + } + + public JsonNode exportDIP( final VitamContext vitamContext, DipRequest dipRequest) + throws VitamClientException { + RequestResponse<JsonNode> response = exportDipV2Service.exportDip( vitamContext, dipRequest); + return response.toJsonNode(); + } + + public JsonNode startEliminationAnalysis(final SearchCriteriaDto searchQuery, final VitamContext vitamContext) + throws VitamClientException { + LOGGER.debug("Elimination analysis by criteria {} ", searchQuery.toString()); + searchQuery.setPageNumber(0); + searchQuery.setSize(EXPORT_DIP_MAX_ELEMENTS); + archiveSearchAgenciesInternalService.mapAgenciesNameToCodes(searchQuery, vitamContext); + archiveSearchRulesInternalService.mapAppraisalRulesTitlesToCodes(searchQuery, vitamContext); + JsonNode dslQuery = mapRequestToDslQuery(searchQuery); + + EliminationRequestBody eliminationRequestBody = null; + try { + eliminationRequestBody = getEliminationRequestBody(dslQuery); + } catch (InvalidParseOperationException e) { + throw new PreconditionFailedException("invalid request"); + } + + LOGGER.debug("Elimination final query {} ", JsonHandler.prettyPrint(eliminationRequestBody.getDslRequest())); + RequestResponse<JsonNode> jsonNodeRequestResponse = + eliminationService.startEliminationAnalysis(vitamContext, eliminationRequestBody); + + + return jsonNodeRequestResponse.toJsonNode(); + } + + public EliminationRequestBody getEliminationRequestBody(JsonNode updateSet) throws InvalidParseOperationException { + ObjectNode query = JsonHandler.createObjectNode(); + query.set(BuilderToken.GLOBAL.ROOTS.exactToken(), updateSet.get(BuilderToken.GLOBAL.ROOTS.exactToken())); + query.set(BuilderToken.GLOBAL.QUERY.exactToken(), updateSet.get(BuilderToken.GLOBAL.QUERY.exactToken())); + query.put(BuilderToken.GLOBAL.THRESOLD.exactToken(), ELIMINATION_ANALYSIS_THRESHOLD); + + EliminationRequestBody requestBody = new EliminationRequestBody(); + requestBody.setDate(new SimpleDateFormat(ArchiveSearchConsts.ONLY_DATE_FORMAT).format(new Date())); + requestBody.setDslRequest(query); + return requestBody; + } + } diff --git a/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/service/ArchivesSearchFieldsQueryBuilderService.java b/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/service/ArchivesSearchFieldsQueryBuilderService.java index 9d7eccf3da9f067e4ab7f46b0ed24eb8ce5879e4..19c23fd7bcb7e7e27738a7b6ded7744f86776943 100644 --- a/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/service/ArchivesSearchFieldsQueryBuilderService.java +++ b/api/api-archive-search/archive-search-internal/src/main/java/fr/gouv/vitamui/archive/internal/server/service/ArchivesSearchFieldsQueryBuilderService.java @@ -69,6 +69,11 @@ public class ArchivesSearchFieldsQueryBuilderService implements IArchivesSearchA searchCriteria.getValues().stream().map(value -> value.getValue()).collect( Collectors.toList()), ArchiveSearchConsts.CriteriaOperators.valueOf(searchCriteria.getOperator()))); + } else if (ArchiveSearchConsts.ELIMINATION_TECHNICAL_ID.equals(searchCriteria.getCriteria())) { + queryToFill.add(buildEliminationAnalysisSearchQuery( + searchCriteria.getValues().stream().map(value -> value.getValue()).collect( + Collectors.toList()), + ArchiveSearchConsts.CriteriaOperators.valueOf(searchCriteria.getOperator()))); } else { String mappedCriteriaName = ArchiveSearchConsts.SIMPLE_FIELDS_VALUES_MAPPING.containsKey(searchCriteria.getCriteria()) ? @@ -100,5 +105,20 @@ public class ArchivesSearchFieldsQueryBuilderService implements IArchivesSearchA return subQueryAnd; } + private Query buildEliminationAnalysisSearchQuery(final List<String> searchValues, + ArchiveSearchConsts.CriteriaOperators operator) + throws InvalidCreateOperationException { + BooleanQuery subQueryAnd = and(); + if (!CollectionUtils.isEmpty(searchValues)) { + for (String value : searchValues) { + BooleanQuery subQueryOr = or(); + subQueryOr + .add(VitamQueryHelper.buildSubQueryByOperator(ArchiveSearchConsts.ELIMINATION_GUID, value, operator)); + subQueryAnd.add(subQueryOr); + } + } + return subQueryAnd; + } + } diff --git a/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/config/ApiArchiveSearchInternalServerConfigTest.java b/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/config/ApiArchiveSearchInternalServerConfigTest.java index a8efdef524d037ebcda756332a09536e1e1ab914..ca53f8661316f9866359c94541a79198e63df8c7 100644 --- a/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/config/ApiArchiveSearchInternalServerConfigTest.java +++ b/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/config/ApiArchiveSearchInternalServerConfigTest.java @@ -28,6 +28,7 @@ package fr.gouv.vitamui.archive.internal.server.config; import fr.gouv.vitam.access.external.client.AccessExternalClient; import fr.gouv.vitam.access.external.client.AdminExternalClient; +import fr.gouv.vitam.access.external.client.v2.AccessExternalClientV2; import fr.gouv.vitam.ingest.external.client.IngestExternalClient; import fr.gouv.vitamui.archive.internal.server.service.ArchiveSearchAgenciesInternalService; import fr.gouv.vitamui.archive.internal.server.service.ArchiveSearchInternalService; @@ -56,6 +57,9 @@ public class ApiArchiveSearchInternalServerConfigTest extends AbstractContextCon @MockBean(name = "accessExternalClient") private AccessExternalClient accessExternalClient; + @MockBean(name = "accessExternalClientV2") + private AccessExternalClientV2 accessExternalClientV2; + @MockBean(name = "ingestExternalClient") private IngestExternalClient ingestExternalClient; diff --git a/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/config/SearchCriteriaHistoryInternalServerConfigTest.java b/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/config/SearchCriteriaHistoryInternalServerConfigTest.java index a96be83b5bc1642e0c0aa4846be7f023e0b72f4a..0897dc483faf91d6799ed10086e64b5b3bdb49f1 100644 --- a/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/config/SearchCriteriaHistoryInternalServerConfigTest.java +++ b/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/config/SearchCriteriaHistoryInternalServerConfigTest.java @@ -39,6 +39,7 @@ package fr.gouv.vitamui.archive.internal.server.config; import fr.gouv.vitam.access.external.client.AccessExternalClient; import fr.gouv.vitam.access.external.client.AdminExternalClient; +import fr.gouv.vitam.access.external.client.v2.AccessExternalClientV2; import fr.gouv.vitam.ingest.external.client.IngestExternalClient; import fr.gouv.vitamui.archive.internal.server.searchcriteria.service.SearchCriteriaHistoryInternalService; import org.junit.jupiter.api.Assertions; @@ -67,6 +68,9 @@ public class SearchCriteriaHistoryInternalServerConfigTest { @MockBean(name = "ingestExternalClient") private IngestExternalClient ingestExternalClient; + @MockBean(name = "accessExternalClientV2") + private AccessExternalClientV2 accessExternalClientV2; + @Autowired private SearchCriteriaHistoryInternalService searchCriteriaHistoryInternalService; 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 d852002e139315032d31892c4f1acde172b77bca..e3203460c5a7aa0089e15cba0361a817026cbd12 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 @@ -41,13 +41,19 @@ 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.PropertiesUtils; +import fr.gouv.vitam.common.database.builder.request.configuration.BuilderToken; import fr.gouv.vitam.common.exception.InvalidParseOperationException; import fr.gouv.vitam.common.exception.VitamClientException; +import fr.gouv.vitam.common.json.JsonHandler; import fr.gouv.vitam.common.model.RequestResponse; import fr.gouv.vitam.common.model.RequestResponseOK; +import fr.gouv.vitam.common.model.elimination.EliminationRequestBody; import fr.gouv.vitamui.commons.api.logger.VitamUILogger; import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory; import fr.gouv.vitamui.commons.test.utils.ServerIdentityConfigurationBuilder; +import fr.gouv.vitamui.commons.vitam.api.access.EliminationService; +import fr.gouv.vitamui.commons.vitam.api.access.ExportDipV2Service; import fr.gouv.vitamui.commons.vitam.api.access.UnitService; import fr.gouv.vitamui.commons.vitam.api.administration.AgencyService; import fr.gouv.vitamui.commons.vitam.api.dto.VitamUISearchResponseDto; @@ -94,7 +100,16 @@ public class ArchiveSearchInternalServiceTest { @InjectMocks private ArchiveSearchInternalService archiveSearchInternalService; + @MockBean(name = "exportDipV2Service") + private ExportDipV2Service exportDipV2Service; + + @MockBean(name = "eliminationService") + private EliminationService eliminationService; + public final String FILING_HOLDING_SCHEME_RESULTS = "data/vitam_filing_holding_units_response.json"; + public final String ELIMINATION_ANALYSIS_QUERY = "data/elimination/query.json"; + public final String ELIMINATION_ANALYSIS_FINAL_QUERY = "data/elimination/expected_query.json"; + public final String ELIMINATION_ANALYSIS_FINAL_RESPONSE = "data/elimination/elimination_analysis_response.json"; @BeforeEach public void setUp() { @@ -102,7 +117,7 @@ public class ArchiveSearchInternalServiceTest { archiveSearchInternalService = new ArchiveSearchInternalService(objectMapper, unitService, archiveSearchAgenciesInternalService, archiveSearchRulesInternalService, archivesSearchFieldsQueryBuilderService, - archivesSearchAppraisalQueryBuilderService); + exportDipV2Service, archivesSearchAppraisalQueryBuilderService, eliminationService); } @Test @@ -136,4 +151,14 @@ public class ArchiveSearchInternalServiceTest { .getFromJsonNode(objectMapper.readValue(ByteStreams.toByteArray(inputStream), JsonNode.class)); } + @Test + public void getFinalEliminationConstructedQuery() throws Exception { + JsonNode fromString = JsonHandler.getFromFile(PropertiesUtils.findFile(ELIMINATION_ANALYSIS_QUERY)); + EliminationRequestBody eliminationRequestBody2 = + archiveSearchInternalService.getEliminationRequestBody(fromString); + + JsonNode resultExpected = JsonHandler.getFromFile(PropertiesUtils.findFile(ELIMINATION_ANALYSIS_FINAL_QUERY)); + Assertions.assertThat(eliminationRequestBody2.getDslRequest()).isEqualTo(resultExpected); + Assertions.assertThat(resultExpected.get(BuilderToken.GLOBAL.THRESOLD.exactToken()).asDouble()).isEqualTo(10000); + } } diff --git a/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/service/ArchivesSearchAppraisalQueryBuilderServiceTest.java b/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/service/ArchivesSearchAppraisalQueryBuilderServiceTest.java index b173d4835d55dbaced59ee87fd2af078b9f19a3e..282df05e02f5a69dd48efa18d5145df8481817e0 100644 --- a/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/service/ArchivesSearchAppraisalQueryBuilderServiceTest.java +++ b/api/api-archive-search/archive-search-internal/src/test/java/fr/gouv/vitamui/archive/internal/server/service/ArchivesSearchAppraisalQueryBuilderServiceTest.java @@ -27,8 +27,11 @@ package fr.gouv.vitamui.archive.internal.server.service; +import com.fasterxml.jackson.databind.JsonNode; +import fr.gouv.vitam.common.PropertiesUtils; import fr.gouv.vitam.common.database.builder.query.BooleanQuery; import fr.gouv.vitam.common.database.builder.request.exception.InvalidCreateOperationException; +import fr.gouv.vitam.common.json.JsonHandler; import fr.gouv.vitamui.archives.search.common.common.ArchiveSearchConsts; import fr.gouv.vitamui.archives.search.common.dto.CriteriaValue; import fr.gouv.vitamui.archives.search.common.dto.SearchCriteriaEltDto; @@ -40,6 +43,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; +import org.skyscreamer.jsonassert.JSONAssert; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.io.BufferedReader; @@ -62,11 +66,15 @@ public class ArchivesSearchAppraisalQueryBuilderServiceTest { @InjectMocks private ArchivesSearchAppraisalQueryBuilderService archivesSearchAppraisalQueryBuilderService; + @InjectMocks + private ArchivesSearchFieldsQueryBuilderService archivesSearchFieldsQueryBuilderService; + @BeforeEach public void setUp() { ServerIdentityConfigurationBuilder.setup("identityName", "identityRole", 1, 0); archivesSearchAppraisalQueryBuilderService = new ArchivesSearchAppraisalQueryBuilderService(); + archivesSearchFieldsQueryBuilderService = new ArchivesSearchFieldsQueryBuilderService(); } @@ -415,6 +423,29 @@ public class ArchivesSearchAppraisalQueryBuilderServiceTest { } + @Test + public void testFillQueryFromCriteriaListWhenEliminationAnalysisTechnicalIdentifierThenReturnTheExactQuery() throws Exception { + //Given + List<SearchCriteriaEltDto> criteriaList = new ArrayList<>(); + SearchCriteriaEltDto searchCriteriaEltDto = new SearchCriteriaEltDto(); + searchCriteriaEltDto.setCriteria(ArchiveSearchConsts.ELIMINATION_TECHNICAL_ID); + searchCriteriaEltDto.setCategory(ArchiveSearchConsts.CriteriaCategory.FIELDS); + searchCriteriaEltDto.setValues(List.of(new CriteriaValue("guid"))); + searchCriteriaEltDto.setOperator(ArchiveSearchConsts.CriteriaOperators.EQ.name()); + criteriaList.add(searchCriteriaEltDto); + + //When + BooleanQuery query = and(); + archivesSearchFieldsQueryBuilderService + .fillQueryFromCriteriaList(query, criteriaList); + + //then + Assertions.assertFalse(query.getQueries().isEmpty()); + JsonNode expectedQuery = + JsonHandler.getFromFile(PropertiesUtils.findFile("data/queries/elimination_analysis_search.json")); + JSONAssert.assertEquals(expectedQuery.toPrettyString(), JsonHandler.getFromString(query.toString()).toPrettyString(), true); + } + private String loadFileContent(String filename) throws IOException { InputStream inputStream = ArchivesSearchAppraisalQueryBuilderServiceTest.class.getClassLoader() .getResourceAsStream("data/queries/" + filename); diff --git a/api/api-archive-search/archive-search-internal/src/test/resources/data/elimination/expected_query.json b/api/api-archive-search/archive-search-internal/src/test/resources/data/elimination/expected_query.json new file mode 100644 index 0000000000000000000000000000000000000000..6bc0dd63f86887975fb82a961529c070b97d2b03 --- /dev/null +++ b/api/api-archive-search/archive-search-internal/src/test/resources/data/elimination/expected_query.json @@ -0,0 +1,93 @@ +{ + "$roots": [], + "$query": [ + { + "$and": [ + { + "$eq": { + "Title": "a" + } + }, + { + "$or": [ + { + "$eq": { + "DescriptionLevel": "RecordGrp" + } + }, + { + "$eq": { + "DescriptionLevel": "File" + } + }, + { + "$eq": { + "DescriptionLevel": "Item" + } + }, + { + "$eq": { + "DescriptionLevel": "Subfonds" + } + }, + { + "$eq": { + "DescriptionLevel": "Class" + } + }, + { + "$eq": { + "DescriptionLevel": "Subgrp" + } + }, + { + "$eq": { + "DescriptionLevel": "Otherlevel" + } + }, + { + "$eq": { + "DescriptionLevel": "Series" + } + }, + { + "$eq": { + "DescriptionLevel": "Subseries" + } + }, + { + "$eq": { + "DescriptionLevel": "Collection" + } + }, + { + "$eq": { + "DescriptionLevel": "Fonds" + } + } + ] + }, + { + "$or": [ + { + "$eq": { + "#id": "aeaqaaaaaefwvz6caasnsalp43nxebyaaaba" + } + }, + { + "$eq": { + "#id": "aeaqaaaaaefwvz6caasnsalp43nxebyaaaba" + } + } + ] + }, + { + "$eq": { + "#unitType": "INGEST" + } + } + ] + } + ], + "$threshold": 10000 +} diff --git a/api/api-archive-search/archive-search-internal/src/test/resources/data/elimination/query.json b/api/api-archive-search/archive-search-internal/src/test/resources/data/elimination/query.json new file mode 100644 index 0000000000000000000000000000000000000000..82a4c0d334e5dd2c36c688199aac90301757e361 --- /dev/null +++ b/api/api-archive-search/archive-search-internal/src/test/resources/data/elimination/query.json @@ -0,0 +1,109 @@ +{ + "$roots": [], + "$query": [ + { + "$and": [ + { + "$eq": { + "Title": "a" + } + }, + { + "$or": [ + { + "$eq": { + "DescriptionLevel": "RecordGrp" + } + }, + { + "$eq": { + "DescriptionLevel": "File" + } + }, + { + "$eq": { + "DescriptionLevel": "Item" + } + }, + { + "$eq": { + "DescriptionLevel": "Subfonds" + } + }, + { + "$eq": { + "DescriptionLevel": "Class" + } + }, + { + "$eq": { + "DescriptionLevel": "Subgrp" + } + }, + { + "$eq": { + "DescriptionLevel": "Otherlevel" + } + }, + { + "$eq": { + "DescriptionLevel": "Series" + } + }, + { + "$eq": { + "DescriptionLevel": "Subseries" + } + }, + { + "$eq": { + "DescriptionLevel": "Collection" + } + }, + { + "$eq": { + "DescriptionLevel": "Fonds" + } + } + ] + }, + { + "$or": [ + { + "$eq": { + "#id": "aeaqaaaaaefwvz6caasnsalp43nxebyaaaba" + } + }, + { + "$eq": { + "#id": "aeaqaaaaaefwvz6caasnsalp43nxebyaaaba" + } + } + ] + }, + { + "$eq": { + "#unitType": "INGEST" + } + } + ] + } + ], + "$filter": { + "$orderby": { + "Title": 1 + }, + "$limit": 10000 + }, + "$projection": {}, + "$facets": [ + { + "$name": "COUNT_BY_NODE", + "$terms": { + "$field": "#allunitups", + "$size": 100, + "$order": "ASC" + } + } + ] +} diff --git a/api/api-archive-search/archive-search-internal/src/test/resources/data/queries/elimination_analysis_search.json b/api/api-archive-search/archive-search-internal/src/test/resources/data/queries/elimination_analysis_search.json new file mode 100644 index 0000000000000000000000000000000000000000..b69f962d90bdf69640f3e0b3098e1c5a36e9630a --- /dev/null +++ b/api/api-archive-search/archive-search-internal/src/test/resources/data/queries/elimination_analysis_search.json @@ -0,0 +1,13 @@ +{ + "$and": [ + { + "$or": [ + { + "$eq": { + "#elimination.OperationId": "guid" + } + } + ] + } + ] +} diff --git a/api/api-iam/iam-internal/src/main/config/customer-init.yml b/api/api-iam/iam-internal/src/main/config/customer-init.yml index 3944169f0c8d2b3df6879b2560e6ca5e62e3656d..c78299d918828f1e8a91ac84ba51b55a43a13ca8 100644 --- a/api/api-iam/iam-internal/src/main/config/customer-init.yml +++ b/api/api-iam/iam-internal/src/main/config/customer-init.yml @@ -151,8 +151,8 @@ customer-init: - ROLE_EDIT_EXTERNAL_PARAM_PROFILE - ROLE_SEARCH_EXTERNAL_PARAM_PROFILE - - name: Profil pour l'accès et la recherche d'archives avec mises à jour des règles et avec export DIP - description: Gestion d'accès et de recherche des archives dans Vitam avec mises à jour des règles et avec export DIP + - name: Recherche et consultation, règles, DIP + description: Gestion d'accès et de recherche des archives dans Vitam avec mises à jour des règles et avec export DIP et sans élimination app-name: ARCHIVE_SEARCH_MANAGEMENT_APP level: roles: @@ -162,8 +162,8 @@ customer-init: - ROLE_SEARCH_WITH_RULES - ROLE_EXPORT_DIP - - name: Profil pour l'accès et la recherche d'archives sans mises à jour des règles et sans export DIP - description: Gestion d'accès et de recherche des archives dans Vitam sans mises à jour des règles et sans export DIP + - name: Recherche et consultation + description: Gestion d'accès et de recherche des archives dans Vitam sans mises à jour des règles, sans export DIP et sans élimination app-name: ARCHIVE_SEARCH_MANAGEMENT_APP level: roles: @@ -171,8 +171,8 @@ customer-init: - ROLE_GET_ARCHIVE_SEARCH - ROLE_GET_ALL_ARCHIVE_SEARCH - - name: Profil pour la recherche et consultation des archives avec export DIP et sans mises à jour des règles - description: Gestion d'accès et de recherche des archives dans Vitam avec export DIP et sans mises à jour des règles + - name: Recherche et consultation, DIP + description: Gestion d'accès et de recherche des archives dans Vitam avec export DIP et sans mises à jour des règles et sans élimination app-name: ARCHIVE_SEARCH_MANAGEMENT_APP level: roles: @@ -181,8 +181,8 @@ customer-init: - ROLE_GET_ALL_ARCHIVE_SEARCH - ROLE_EXPORT_DIP - - name: Profil pour la recherche et consultation des archives avec mises à jour des règles et sans export DIP - description: Gestion d'accès et de recherche des archives dans Vitam avec mises à jour des règles et sans export DIP + - name: Recherche et consultation, règles + description: Gestion d'accès et de recherche des archives dans Vitam avec mises à jour des règles, sans export DIP et sans élimination app-name: ARCHIVE_SEARCH_MANAGEMENT_APP level: roles: @@ -191,6 +191,39 @@ customer-init: - ROLE_GET_ALL_ARCHIVE_SEARCH - ROLE_SEARCH_WITH_RULES + - name: Recherche et consultation, règles, élimination + description: Gestion d'accès et de recherche des archives dans Vitam avec mises à jour des règles, opérations d'élimination et sans export DIP + app-name: ARCHIVE_SEARCH_MANAGEMENT_APP + level: + roles: + - ROLE_CREATE_ARCHIVE_SEARCH + - ROLE_GET_ARCHIVE_SEARCH + - ROLE_GET_ALL_ARCHIVE_SEARCH + - ROLE_SEARCH_WITH_RULES + - ROLE_ELIMINATION + + - name: Recherche et consultation, DIP, élimination + description: Gestion d'accès et de recherche des archives dans Vitam avec export DIP, opérations d'éliminations et sans mise à jour des règles + app-name: ARCHIVE_SEARCH_MANAGEMENT_APP + level: + roles: + - ROLE_CREATE_ARCHIVE_SEARCH + - ROLE_GET_ARCHIVE_SEARCH + - ROLE_GET_ALL_ARCHIVE_SEARCH + - ROLE_EXPORT_DIP + - ROLE_ELIMINATION + + - name: Recherche et consultation, règles, DIP, élimination + description: Gestion d'accès et de recherche des archives dans Vitam avec mises à jour des règles, export DIP et opérations d'élimination + app-name: ARCHIVE_SEARCH_MANAGEMENT_APP + level: + roles: + - ROLE_CREATE_ARCHIVE_SEARCH + - ROLE_GET_ARCHIVE_SEARCH + - ROLE_GET_ALL_ARCHIVE_SEARCH + - ROLE_SEARCH_WITH_RULES + - ROLE_EXPORT_DIP + - ROLE_ELIMINATION #- name: profileName # description: desc # level: 1 diff --git a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/config/ApiIamServerConfigTest.java b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/config/ApiIamServerConfigTest.java index c460fb837954c2b5243578f9048cda50dc1ae08b..259d89ac8ab39e9256179585371081ebc4e9e550 100644 --- a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/config/ApiIamServerConfigTest.java +++ b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/config/ApiIamServerConfigTest.java @@ -2,6 +2,7 @@ package fr.gouv.vitamui.iam.internal.server.config; import static org.assertj.core.api.Assertions.assertThat; +import fr.gouv.vitam.access.external.client.v2.AccessExternalClientV2; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -31,6 +32,9 @@ public class ApiIamServerConfigTest { @MockBean(name = "ingestExternalClient") private IngestExternalClient ingestExternalClient; + @MockBean(name = "accessExternalClientV2") + private AccessExternalClientV2 accessExternalClientV2; + @Autowired private EventService logbookService; diff --git a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/customer/service/InitCustomerServiceIntegrationTest.java b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/customer/service/InitCustomerServiceIntegrationTest.java index 719054cdc2aeb7661b37f211202bdd1ce9234b1d..876c45add2aca11c2689b38c0718f1e7f4e33369 100644 --- a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/customer/service/InitCustomerServiceIntegrationTest.java +++ b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/customer/service/InitCustomerServiceIntegrationTest.java @@ -2,6 +2,7 @@ package fr.gouv.vitamui.iam.internal.server.customer.service; import fr.gouv.vitam.access.external.client.AccessExternalClient; import fr.gouv.vitam.access.external.client.AdminExternalClient; +import fr.gouv.vitam.access.external.client.v2.AccessExternalClientV2; import fr.gouv.vitam.ingest.external.client.IngestExternalClient; import fr.gouv.vitamui.commons.api.domain.AddressDto; import fr.gouv.vitamui.commons.api.domain.ExternalParametersDto; @@ -128,6 +129,9 @@ public class InitCustomerServiceIntegrationTest { @MockBean(name = "ingestExternalClient") private IngestExternalClient ingestExternalClient; + @MockBean(name = "accessExternalClientV2") + private AccessExternalClientV2 accessExternalClientV2; + @MockBean private InternalSecurityService internalSecurityService; diff --git a/api/api-ingest/ingest-internal/src/test/java/fr/gouv/vitamui/ingest/internal/server/config/ApiIngestInternalServerConfigTest.java b/api/api-ingest/ingest-internal/src/test/java/fr/gouv/vitamui/ingest/internal/server/config/ApiIngestInternalServerConfigTest.java index aa87cc184b1bea04e181db0d6a23c6c1868924b7..9d2c74987d325310776bbab9e0ab0aa11bec7147 100644 --- a/api/api-ingest/ingest-internal/src/test/java/fr/gouv/vitamui/ingest/internal/server/config/ApiIngestInternalServerConfigTest.java +++ b/api/api-ingest/ingest-internal/src/test/java/fr/gouv/vitamui/ingest/internal/server/config/ApiIngestInternalServerConfigTest.java @@ -38,6 +38,7 @@ package fr.gouv.vitamui.ingest.internal.server.config; import fr.gouv.vitam.access.external.client.AccessExternalClient; import fr.gouv.vitam.access.external.client.AdminExternalClient; +import fr.gouv.vitam.access.external.client.v2.AccessExternalClientV2; import fr.gouv.vitam.ingest.external.client.IngestExternalClient; import fr.gouv.vitamui.commons.vitam.api.ingest.IngestService; import fr.gouv.vitamui.ingest.internal.server.service.IngestInternalService; @@ -67,6 +68,9 @@ public class ApiIngestInternalServerConfigTest { @MockBean(name = "ingestExternalClient") private IngestExternalClient ingestExternalClient; + @MockBean(name = "accessExternalClientV2") + private AccessExternalClientV2 accessExternalClientV2; + @MockBean(name = "ingestService") private IngestService ingestService; diff --git a/commons/commons-api/src/main/java/fr/gouv/vitamui/commons/api/domain/ServicesData.java b/commons/commons-api/src/main/java/fr/gouv/vitamui/commons/api/domain/ServicesData.java index 0a150834a5a277da03d2c64bb49a648c1c7ee108..cb3ca7c79999dee8d5823aafdef9f5c898ab1b01 100644 --- a/commons/commons-api/src/main/java/fr/gouv/vitamui/commons/api/domain/ServicesData.java +++ b/commons/commons-api/src/main/java/fr/gouv/vitamui/commons/api/domain/ServicesData.java @@ -367,7 +367,8 @@ public class ServicesData { public static final String ROLE_GET_ARCHIVE = GET_ROLE_PREFIX + SERVICE_ARCHIVE; public static final String ROLE_GET_ALL_ARCHIVE = GET_ROLE_PREFIX + "ALL_" + SERVICE_ARCHIVE; public static final String ROLE_SEARCH_WITH_RULES = ROLE_PREFIX + "SEARCH_WITH_RULES"; - public static final String ROLE_EXPORT_DIP = ROLE_PREFIX + "EXPORT-DIP"; + public static final String ROLE_EXPORT_DIP = ROLE_PREFIX + "EXPORT_DIP"; + public static final String ROLE_ELIMINATION_ANALYSIS = ROLE_PREFIX + "ELIMINATION"; //------------------------------------ API TREES & PLANS ----------------------------------------- diff --git a/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/access/EliminationService.java b/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/access/EliminationService.java new file mode 100644 index 0000000000000000000000000000000000000000..36f8a0659aa9c42d2bb52f67d57471af3c64e63e --- /dev/null +++ b/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/access/EliminationService.java @@ -0,0 +1,94 @@ +/** + * 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.access; + +import com.fasterxml.jackson.databind.JsonNode; +import fr.gouv.vitam.access.external.client.AccessExternalClient; +import fr.gouv.vitam.common.client.VitamContext; +import fr.gouv.vitam.common.exception.VitamClientException; +import fr.gouv.vitam.common.model.RequestResponse; +import fr.gouv.vitam.common.model.elimination.EliminationRequestBody; +import fr.gouv.vitamui.commons.api.logger.VitamUILogger; +import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory; +import fr.gouv.vitamui.commons.vitam.api.util.VitamRestUtils; +import org.apache.http.HttpStatus; +import org.springframework.beans.factory.annotation.Autowired; + + +/** + * Service de lancement des workflows d'élimination d'analyse et d'action + * Pour plus d'informations : <a href="http://www.programmevitam.fr/ressources/DocCourante/autres/fonctionnel/VITAM_Eliminations.pdf">documentation métier</a> + */ +public class EliminationService { + + @SuppressWarnings("unused") + private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(EliminationService.class); + + private final AccessExternalClient accessExternalClient; + + @Autowired + public EliminationService(final AccessExternalClient accessExternalClient) { + this.accessExternalClient = accessExternalClient; + } + + /** + * Starts the elimination analysis of the units by dsl query. + * @param vitamContext The vitam context + * @param eliminationRequestBody The DSL query used to select units to which elimination analysis would be launched + * @return + * @throws VitamClientException + */ + public RequestResponse<JsonNode> startEliminationAnalysis(final VitamContext vitamContext, final EliminationRequestBody eliminationRequestBody) throws VitamClientException { + final RequestResponse<JsonNode> response = accessExternalClient.startEliminationAnalysis(vitamContext, eliminationRequestBody); + VitamRestUtils.checkResponse(response, HttpStatus.SC_OK, HttpStatus.SC_ACCEPTED); + return response; + } + + /** + * Starts the elimination action of the units by dsl query. + * @param vitamContext The vitam context + * @param eliminationRequestBody The DSL query used to select units to which elimination action would be launched + * @return + * @throws VitamClientException + */ + public RequestResponse<JsonNode> startEliminationAction(final VitamContext vitamContext, final EliminationRequestBody eliminationRequestBody) throws VitamClientException { + final RequestResponse<JsonNode> response = accessExternalClient.startEliminationAction(vitamContext, eliminationRequestBody); + VitamRestUtils.checkResponse(response, HttpStatus.SC_OK, HttpStatus.SC_ACCEPTED); + return response; + } +} diff --git a/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/access/ExportDipV2Service.java b/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/access/ExportDipV2Service.java new file mode 100644 index 0000000000000000000000000000000000000000..a20a1865751003b447f83460949e5ef48baec71a --- /dev/null +++ b/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/access/ExportDipV2Service.java @@ -0,0 +1,75 @@ +/** + * 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.access; + +import com.fasterxml.jackson.databind.JsonNode; +import fr.gouv.vitam.access.external.client.v2.AccessExternalClientV2; +import fr.gouv.vitam.common.client.VitamContext; +import fr.gouv.vitam.common.exception.VitamClientException; +import fr.gouv.vitam.common.model.RequestResponse; +import fr.gouv.vitam.common.model.export.dip.DipRequest; +import fr.gouv.vitamui.commons.api.logger.VitamUILogger; +import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory; +import fr.gouv.vitamui.commons.vitam.api.util.VitamRestUtils; +import org.apache.http.HttpStatus; + +public class ExportDipV2Service { + + @SuppressWarnings("unused") + private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(ExportDipV2Service.class); + + private final AccessExternalClientV2 accessExternalClientV2; + + public ExportDipV2Service(final AccessExternalClientV2 accessExternalClientV2) { + this.accessExternalClientV2 = accessExternalClientV2; + } + + /** + * Starts the export of the files of the selected unit archives. + * @param dipRequest DipRequest used to select units to export as DIP + * @param vitamContext The vitam context + * @return + * @throws VitamClientException + */ + + public RequestResponse<JsonNode> exportDip( final VitamContext vitamContext, final DipRequest dipRequest) throws VitamClientException { + final RequestResponse<JsonNode> response = accessExternalClientV2.exportDIP(vitamContext, dipRequest); + VitamRestUtils.checkResponse(response, HttpStatus.SC_OK, HttpStatus.SC_ACCEPTED); + return response; + } +} diff --git a/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/access/UnitService.java b/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/access/UnitService.java index 908e260a1eb12f975e699ecad3882673532bcc15..970c95dee29200a9d960b9444e7c18b6a9437427 100644 --- a/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/access/UnitService.java +++ b/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/access/UnitService.java @@ -87,7 +87,7 @@ public class UnitService { return result; } - public RequestResponse<JsonNode> searchUnitsWithErrors(final Optional<String> unitId, final JsonNode dslQuery, final VitamContext vitamContext) + public RequestResponse<JsonNode> searchUnitsWithErrors(final Optional<String> unitId, final JsonNode dslQuery, final VitamContext vitamContext) throws VitamClientException { final RequestResponse<JsonNode> result; if (unitId.isPresent()) { @@ -97,7 +97,7 @@ public class UnitService { } return result; } - + public RequestResponse<JsonNode> searchUnitsWithInheritedRules(final JsonNode dslQuery, final VitamContext vitamContext) throws VitamClientException { final RequestResponse<JsonNode> result = @@ -114,7 +114,7 @@ public class UnitService { VitamRestUtils.checkResponse(result); return result; } - + public RequestResponse<JsonNode> findObjectMetadataById(final String unitId, final JsonNode dslQuery, final VitamContext vitamContext) throws VitamClientException { final RequestResponse<JsonNode> result = accessExternalClient.selectObjectMetadatasByUnitId(vitamContext, dslQuery, unitId); VitamRestUtils.checkResponse(result); diff --git a/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/config/VitamAccessConfig.java b/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/config/VitamAccessConfig.java index 850069bdfce03ac914f19aa8d885108add9139f8..d2f2d37e971132175ee1689d35aee16c1f19a4da 100644 --- a/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/config/VitamAccessConfig.java +++ b/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/config/VitamAccessConfig.java @@ -36,6 +36,8 @@ */ package fr.gouv.vitamui.commons.vitam.api.config; +import fr.gouv.vitamui.commons.vitam.api.access.ExportDipV2Service; +import fr.gouv.vitamui.commons.vitam.api.access.EliminationService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -67,4 +69,13 @@ public class VitamAccessConfig extends VitamClientConfig { return new ExportDipService(accessExternalClient()); } + @Bean + public ExportDipV2Service getExportDipV2Service() { + return new ExportDipV2Service(accessExternalClientV2()); + } + + @Bean + public EliminationService getEliminationService() { + return new EliminationService(accessExternalClient()); + } } diff --git a/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/config/VitamClientConfig.java b/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/config/VitamClientConfig.java index be9789fa876bada7a704627a584ce03fb62db14e..a62830f27d46c287d1d0481daebdb6461fe168ec 100644 --- a/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/config/VitamClientConfig.java +++ b/commons/commons-vitam/src/main/java/fr/gouv/vitamui/commons/vitam/api/config/VitamClientConfig.java @@ -36,6 +36,8 @@ */ package fr.gouv.vitamui.commons.vitam.api.config; +import fr.gouv.vitam.access.external.client.v2.AccessExternalClientV2; +import fr.gouv.vitam.access.external.client.v2.AccessExternalClientV2Factory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Profile; @@ -79,4 +81,13 @@ public abstract class VitamClientConfig { } return factory.getClient(); } + + @Bean + public AccessExternalClientV2 accessExternalClientV2() { + final AccessExternalClientV2Factory factory = AccessExternalClientV2Factory.getInstance(); + if (VitamClientType.MOCK.equals(factory.getVitamClientType())) { + throw new InternalServerException("Failed to load Vitam configuration: Vitam client is in MOCK mode"); + } + return factory.getClient(); + } } diff --git a/deployment/roles/vitamui/files/customer-init.yml b/deployment/roles/vitamui/files/customer-init.yml index 6b3b6e145aaacf49469c09f3e811c576e7503d65..7954a017871609b654e10d35abfecdb6f2103a48 100644 --- a/deployment/roles/vitamui/files/customer-init.yml +++ b/deployment/roles/vitamui/files/customer-init.yml @@ -156,8 +156,8 @@ customer-init: - ROLE_EDIT_EXTERNAL_PARAM_PROFILE - ROLE_SEARCH_EXTERNAL_PARAM_PROFILE - - name: Profil pour l'accès et la recherche d'archives avec mises à jour des règles et avec export DIP - description: Gestion d'accès et de recherche des archives dans Vitam avec mises à jour des règles et avec export DIP + - name: Recherche et consultation, règles, DIP + description: Gestion d'accès et de recherche des archives dans Vitam avec mises à jour des règles et avec export DIP et sans élimination app-name: ARCHIVE_SEARCH_MANAGEMENT_APP level: roles: @@ -167,8 +167,8 @@ customer-init: - ROLE_SEARCH_WITH_RULES - ROLE_EXPORT_DIP - - name: Profil pour l'accès et la recherche d'archives sans mises à jour des règles et sans export DIP - description: Gestion d'accès et de recherche des archives dans Vitam sans mises à jour des règles et sans export DIP + - name: Recherche et consultation + description: Gestion d'accès et de recherche des archives dans Vitam sans mises à jour des règles, sans export DIP et sans élimination app-name: ARCHIVE_SEARCH_MANAGEMENT_APP level: roles: @@ -176,8 +176,8 @@ customer-init: - ROLE_GET_ARCHIVE_SEARCH - ROLE_GET_ALL_ARCHIVE_SEARCH - - name: Profil pour la recherche et consultation des archives avec export DIP et sans mises à jour des règles - description: Gestion d'accès et de recherche des archives dans Vitam avec export DIP et sans mises à jour des règles + - name: Recherche et consultation, DIP + description: Gestion d'accès et de recherche des archives dans Vitam avec export DIP et sans mises à jour des règles et sans élimination app-name: ARCHIVE_SEARCH_MANAGEMENT_APP level: roles: @@ -186,8 +186,8 @@ customer-init: - ROLE_GET_ALL_ARCHIVE_SEARCH - ROLE_EXPORT_DIP - - name: Profil pour la recherche et consultation des archives avec mises à jour des règles et sans export DIP - description: Gestion d'accès et de recherche des archives dans Vitam avec mises à jour des règles et sans export DIP + - name: Recherche et consultation, règles + description: Gestion d'accès et de recherche des archives dans Vitam avec mises à jour des règles, sans export DIP et sans élimination app-name: ARCHIVE_SEARCH_MANAGEMENT_APP level: roles: @@ -196,6 +196,40 @@ customer-init: - ROLE_GET_ALL_ARCHIVE_SEARCH - ROLE_SEARCH_WITH_RULES + - name: Recherche et consultation, règles, élimination + description: Gestion d'accès et de recherche des archives dans Vitam avec mises à jour des règles, opérations d'élimination et sans export DIP + app-name: ARCHIVE_SEARCH_MANAGEMENT_APP + level: + roles: + - ROLE_CREATE_ARCHIVE_SEARCH + - ROLE_GET_ARCHIVE_SEARCH + - ROLE_GET_ALL_ARCHIVE_SEARCH + - ROLE_SEARCH_WITH_RULES + - ROLE_ELIMINATION + + - name: Recherche et consultation, DIP, élimination + description: Gestion d'accès et de recherche des archives dans Vitam avec export DIP, opérations d'éliminations et sans mise à jour des règles + app-name: ARCHIVE_SEARCH_MANAGEMENT_APP + level: + roles: + - ROLE_CREATE_ARCHIVE_SEARCH + - ROLE_GET_ARCHIVE_SEARCH + - ROLE_GET_ALL_ARCHIVE_SEARCH + - ROLE_EXPORT_DIP + - ROLE_ELIMINATION + + - name: Recherche et consultation, règles, DIP, élimination + description: Gestion d'accès et de recherche des archives dans Vitam avec mises à jour des règles, export DIP et opérations d'élimination + app-name: ARCHIVE_SEARCH_MANAGEMENT_APP + level: + roles: + - ROLE_CREATE_ARCHIVE_SEARCH + - ROLE_GET_ARCHIVE_SEARCH + - ROLE_GET_ALL_ARCHIVE_SEARCH + - ROLE_SEARCH_WITH_RULES + - ROLE_EXPORT_DIP + - ROLE_ELIMINATION + - name: Registre des fonds description: Visualisation de l'ensemble des données du registre des fonds app-name: ACCESSION_REGISTER_APP diff --git a/deployment/roles/vitamui/templates/ui-archive-search/application.yml.j2 b/deployment/roles/vitamui/templates/ui-archive-search/application.yml.j2 index a640cd0c484a90f6db35498c894a0f7a856b345c..858a4c7635fe799bda45aca5f2e2e56beec4b3b9 100644 --- a/deployment/roles/vitamui/templates/ui-archive-search/application.yml.j2 +++ b/deployment/roles/vitamui/templates/ui-archive-search/application.yml.j2 @@ -105,6 +105,11 @@ ui-archive-search: archive-search: "{{ vitamui.archive_search.base_url }}" {% else %} archive-search: "{{ url_prefix }}/archive-search" +{% endif %} +{% if vitamui.referential.base_url is defined %} + referential: "{{ vitamui.referential.base_url }}" +{% else %} + referential: "{{ url_prefix }}/referential" {% endif %} portal-categories: {% for id, category in vitamui_defaults.portal_categories.items() %} diff --git a/deployment/scripts/mongod/2.0.0/33_update_create_profiles_archive_search_to_add_elimination.js.j2 b/deployment/scripts/mongod/2.0.0/33_update_create_profiles_archive_search_to_add_elimination.js.j2 new file mode 100644 index 0000000000000000000000000000000000000000..3a16962b343ca8b4c2f47d05c9bc8718d856f0d9 --- /dev/null +++ b/deployment/scripts/mongod/2.0.0/33_update_create_profiles_archive_search_to_add_elimination.js.j2 @@ -0,0 +1,109 @@ +db = db.getSiblingDB('iam') + +print("33_update_create_profiles_archive_search_to_add_elimination.js"); + +// -------- ARCHIVE_SEARCH PROFILE WITH RULES MANAGEMENT AND DIP EXPORT AND ELIMINATION OPERATIONS ----- + +db.profiles.updateOne({ + "_id":"system_archive_search_profile" +}, +{ + $addToSet:{ + "roles":{ + $each:[ + { + "name":"ROLE_ELIMINATION" + } + ] + } + } +}); + +db.profiles.update({ + "_id":"system_archive_search_profile" +}, +{ + $set:{ + "description":"Profil pour la recherche et consultation des archives avec mises à jour des règles, export DIP et opérations d'éliminations", + "name":"Profil pour la recherche et consultation des archives avec mises à jour des règles, export DIP et opérations d'éliminations" + } +}); + +// -------- ARCHIVE_SEARCH PROFILE WITH RULES MANAGEMENT AND ELIMINATION OPERATIONS AND WITHOUT DIP EXPORT ----- + +db.profiles.updateOne({ + "_id":"system_archive_search_profile_with_rules_without_export" +}, +{ + $addToSet:{ + "roles":{ + $each:[ + { + "name":"ROLE_ELIMINATION" + } + ] + } + } +}); + +db.profiles.update({ + "_id":"system_archive_search_profile_with_rules_without_export" +}, +{ + $set:{ + "description":"Profil pour la recherche et consultation des archives avec mises à jour des règles et opérations d'éliminations", + "name":"Profil pour la recherche et consultation des archives avec mises à jour des règles et opérations d'éliminations" + } +}); + + +// -------- ARCHIVE_SEARCH PROFILE WITH DIP EXPORT AND ELIMINATION OPERATION BUT WITHOUT RULES MANAGEMENT SEARCH ----- + +db.profiles.updateOne({ + "_id":"system_archive_search_profile_without_rules_with_export" +}, +{ + $addToSet:{ + "roles":{ + $each:[ + { + "name":"ROLE_ELIMINATION" + } + ] + } + } +}); + +db.profiles.insert({ + "_id":"system_archive_search_profile_without_rules_with_export_and_with_elimination", + "identifier":NumberInt(30), + "name":"Profil pour la recherche et consultation des archives avec export DIP et opérations d'éliminations et sans mises à jour des règles", + "description":"Profil pour la recherche et consultation des archives avec export DIP et opérations d'éliminations et sans mises à jour des règles", + "tenantIdentifier":NumberInt({{ vitamui_platform_informations.proof_tenant }}), + "applicationName":"ARCHIVE_SEARCH_MANAGEMENT_APP", + "level":"", + "enabled":true, + "readonly":false, + "customerId":"system_customer", + "roles":[ + { + "name":"ROLE_CREATE_ARCHIVE_SEARCH" + }, + { + "name":"ROLE_GET_ARCHIVE_SEARCH" + }, + { + "name":"ROLE_GET_ALL_ARCHIVE_SEARCH" + }, + { + "name":"ROLE_EXPORT_DIP" + }, + { + "name":"ROLE_ELIMINATION" + } + ] +}); + +print("33_update_create_profiles_archive_search_to_add_elimination.js"); + + diff --git a/deployment/scripts/mongod/2.0.0/34_new_update_archive_search_context_to_add_elimination.js.j2 b/deployment/scripts/mongod/2.0.0/34_new_update_archive_search_context_to_add_elimination.js.j2 new file mode 100644 index 0000000000000000000000000000000000000000..b915334d17cae7e32e2f53e54e9ee79fd4d50897 --- /dev/null +++ b/deployment/scripts/mongod/2.0.0/34_new_update_archive_search_context_to_add_elimination.js.j2 @@ -0,0 +1,18 @@ +db = db.getSiblingDB('security') + +print("34_new_update_archive_search_context_to_add_elimination.js"); + +db.contexts.updateOne({ + "_id":"ui_archive_search_context" +}, +{ + $addToSet:{ + "roleNames":{ + $each:[ + "ROLE_ELIMINATION" + ] + } + } +}); + +print("34_new_update_archive_search_context_to_add_elimination.js"); diff --git a/ui/ui-archive-search/src/main/config/ui-archive-search-application-dev.yml b/ui/ui-archive-search/src/main/config/ui-archive-search-application-dev.yml index 3efd2757f81c140d743779e10fd9984fe6dcb228..722b870c11b33cea5e5a44309a3113e3468efe5a 100644 --- a/ui/ui-archive-search/src/main/config/ui-archive-search-application-dev.yml +++ b/ui/ui-archive-search/src/main/config/ui-archive-search-application-dev.yml @@ -72,6 +72,7 @@ ui-archive-search: portal: "https://dev.vitamui.com:4200" archives-search: "https://dev.vitamui.com:4209/archive-search" ingest: "https://dev.vitamui.com:4208/ingest" + referential: "https://dev.vitamui.com:4202/referential" portal-categories: ingest_and_consultation: 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 6138c5c1f66ccd99e031c13e1e561602b4ec4c03..23a31385ef7541d5b586e4b920a86c6201ed8342 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 @@ -26,8 +26,10 @@ package fr.gouv.vitamui.archives.search.rest; +import com.fasterxml.jackson.databind.JsonNode; import fr.gouv.vitamui.archives.search.common.common.ArchiveSearchConsts; import fr.gouv.vitamui.archives.search.common.dto.ArchiveUnitsDto; +import fr.gouv.vitamui.archives.search.common.dto.ExportDipCriteriaDto; import fr.gouv.vitamui.archives.search.common.dto.ObjectData; import fr.gouv.vitamui.archives.search.common.dto.SearchCriteriaDto; import fr.gouv.vitamui.archives.search.common.dto.VitamUIArchiveUnitResponseDto; @@ -171,4 +173,23 @@ public class ArchivesSearchController extends AbstractUiRestController { .header("Content-Disposition", "attachment") .body(exportedCsvResult); } + + @ApiOperation(value = "export DIP by criteria") + @PostMapping(RestApi.EXPORT_DIP) + @ResponseStatus(HttpStatus.OK) + public String exportDIPByCriteria(@RequestBody final ExportDipCriteriaDto exportDipCriteriaDto) { + LOGGER.debug("Export DIP with criteria {}", exportDipCriteriaDto); + String result = archivesSearchService.exportDIPByCriteria(exportDipCriteriaDto, buildUiHttpContext()).getBody(); + return result; + } + + @ApiOperation(value = "elimination analysis launch") + @PostMapping(RestApi.ELIMINATION_ANALYSIS) + @ResponseStatus(HttpStatus.OK) + public JsonNode startEliminationAnalysis(@RequestBody final SearchCriteriaDto searchQuery) { + LOGGER.debug("Elimination analysis of query: {}", searchQuery); + ResponseEntity<JsonNode> jsonNodeResponseEntity = + archivesSearchService.startEliminationAnalysis(buildUiHttpContext(), searchQuery); + return jsonNodeResponseEntity.getBody(); + } } 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 407117183bd42394ca773e7e21ae60acaa30aa7e..4fe4b0ccbeab298ff8ae5a407743c7cb306ebf4f 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 @@ -26,7 +26,9 @@ package fr.gouv.vitamui.archives.search.service; +import com.fasterxml.jackson.databind.JsonNode; import fr.gouv.vitamui.archives.search.common.dto.ArchiveUnitsDto; +import fr.gouv.vitamui.archives.search.common.dto.ExportDipCriteriaDto; 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; @@ -159,4 +161,14 @@ public class ArchivesSearchService extends AbstractPaginateService<ArchiveUnitsD qualifiers.stream().filter(q -> q.getQualifier().equals(usage)).findFirst().get().getVersions(); return Integer.parseInt(versions.get(0).getDataObjectVersion().split("_")[1]); } + + public ResponseEntity<String> exportDIPByCriteria(final ExportDipCriteriaDto exportDipCriteriaDto,ExternalHttpContext context) { + LOGGER.info("export DIP with criteria {}", exportDipCriteriaDto); + return archiveSearchExternalRestClient.exportDIPCriteria(exportDipCriteriaDto, context); + } + + public ResponseEntity<JsonNode> startEliminationAnalysis(ExternalHttpContext context, final SearchCriteriaDto searchQuery) { + LOGGER.info("elimination analysis with query : {}", searchQuery); + return archiveSearchExternalRestClient.startEliminationAnalysis(context, searchQuery); + } } diff --git a/ui/ui-frontend-common/src/app/modules/security/has-role.directive.ts b/ui/ui-frontend-common/src/app/modules/security/has-role.directive.ts index 517ef7e62100c9157c6f2212661ad80f32ad18db..801a9ac3179da1ff3cd925990322b70c251474d4 100644 --- a/ui/ui-frontend-common/src/app/modules/security/has-role.directive.ts +++ b/ui/ui-frontend-common/src/app/modules/security/has-role.directive.ts @@ -36,27 +36,19 @@ */ import { Directive, Input, OnDestroy, OnInit, TemplateRef, ViewContainerRef } from '@angular/core'; import { Subscription } from 'rxjs'; - import { SecurityService } from './security.service'; @Directive({ - selector: '[vitamuiCommonHasRole]' + selector: '[vitamuiCommonHasRole]', }) export class HasRoleDirective implements OnInit, OnDestroy { - roleSubscription: Subscription; private viewEmbedded = false; - constructor( - private templateRef: TemplateRef<any>, - private viewContainer: ViewContainerRef, - private securityService: SecurityService - ) { - } + constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef, private securityService: SecurityService) {} - ngOnInit(): void { - } + ngOnInit(): void {} ngOnDestroy(): void { if (this.roleSubscription) { @@ -65,13 +57,12 @@ export class HasRoleDirective implements OnInit, OnDestroy { } @Input() - set vitamuiCommonHasRole(data: { appId: string, tenantIdentifier: number, role: string }) { + set vitamuiCommonHasRole(data: { appId: string; tenantIdentifier: number; role: string }) { if (this.roleSubscription) { this.roleSubscription.unsubscribe(); } - this.roleSubscription = this.securityService.hasRole(data.appId, data.tenantIdentifier, data.role) - .subscribe((allowed) => { + this.roleSubscription = this.securityService.hasRole(data.appId, data.tenantIdentifier, data.role).subscribe((allowed) => { if (allowed && !this.viewEmbedded) { this.viewEmbedded = true; this.viewContainer.createEmbeddedView(this.templateRef); @@ -81,5 +72,4 @@ export class HasRoleDirective implements OnInit, OnDestroy { } }); } - } diff --git a/ui/ui-frontend-common/src/app/modules/startup.service.ts b/ui/ui-frontend-common/src/app/modules/startup.service.ts index cefba18602d70c155028027831e29fd207c2ab2a..8483519e2c6277d5b51deb56017673c84b519719 100644 --- a/ui/ui-frontend-common/src/app/modules/startup.service.ts +++ b/ui/ui-frontend-common/src/app/modules/startup.service.ts @@ -226,7 +226,7 @@ export class StartupService { getReferentialUrl(): string { if (this.configurationLoaded()) { - return this.configurationData.REFERENTIAL_SEARCH_URL; + return this.configurationData.REFERENTIAL_URL; } return null; diff --git a/ui/ui-frontend-common/src/app/modules/vitamui-roles.enum.ts b/ui/ui-frontend-common/src/app/modules/vitamui-roles.enum.ts index 8199d3b03c90bfb3e2e6d9f96f270b762ea20012..b791c660f0eb46e6fc58173252d19b44026aa65a 100644 --- a/ui/ui-frontend-common/src/app/modules/vitamui-roles.enum.ts +++ b/ui/ui-frontend-common/src/app/modules/vitamui-roles.enum.ts @@ -30,5 +30,5 @@ export enum VitamuiRoles { ROLE_GET_ARCHIVE_SEARCH = 'ROLE_GET_ARCHIVE_SEARCH', ROLE_GET_ALL_ARCHIVE_SEARCH = 'ROLE_GET_ALL_ARCHIVE_SEARCH', ROLE_SEARCH_WITH_RULES = 'ROLE_SEARCH_WITH_RULES', - ROLE_EXPORT_DIP = 'ROLE_EXPORT_DIP', + ROLE_EXPORT_DIP = 'ROLE_EXPORT_DIP' } diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/appraisal-rule-search/appraisal-rule-search.component.html b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/appraisal-rule-search/appraisal-rule-search.component.html index 6831883c7b28d65f77349e6a88f46a1b544daecd..5f5e88556327f028f2184128b376e2b5c54e7aa6 100644 --- a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/appraisal-rule-search/appraisal-rule-search.component.html +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/appraisal-rule-search/appraisal-rule-search.component.html @@ -43,8 +43,7 @@ >{{ (endDateInterval ? 'ARCHIVE_SEARCH.SEARCH_CRITERIA_FILTER.FIELDS.END_DATE_DUA_BEGIN' - : 'ARCHIVE_SEARCH.SEARCH_CRITERIA_FILTER.FIELDS.END_DATE_DUA_EQUAL' - ) | translate + : 'ARCHIVE_SEARCH.SEARCH_CRITERIA_FILTER.FIELDS.END_DATE_DUA_EQUAL') | translate }}</span > <ng-template #showBeginDtDua diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/appraisal-rule-search/appraisal-rule-search.component.ts b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/appraisal-rule-search/appraisal-rule-search.component.ts index f2d789f100a701c3e239e89ac060cb8fdb83fd1a..02d2866de7841a386bd0ad1c3a89d4639e65e12a 100644 --- a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/appraisal-rule-search/appraisal-rule-search.component.ts +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/appraisal-rule-search/appraisal-rule-search.component.ts @@ -28,6 +28,7 @@ const APPRAISAL_RULE_ORIGIN = 'APPRAISAL_RULE_ORIGIN'; const APPRAISAL_RULE_IDENTIFIER = 'APPRAISAL_RULE_IDENTIFIER'; const APPRAISAL_RULE_TITLE = 'APPRAISAL_RULE_TITLE'; const APPRAISAL_RULE_END_DATE = 'APPRAISAL_RULE_END_DATE'; +const ELIMINATION_TECHNICAL_ID = 'ELIMINATION_TECHNICAL_ID'; @Component({ selector: 'appraisal-rule-search', @@ -56,7 +57,7 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { appraisalRuleFinalActionHasFinalAction: boolean; appraisalRuleFinalActionInheriteFinalAction: boolean; - appraisalRuleEliminationIdentifier?: any; + appraisalRuleEliminationIdentifier?: string; }; emptyAppraisalCriteriaForm = { appraisalRuleIdentifier: '', @@ -72,7 +73,7 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { notSpecifiedFinalActionType: false, appraisalRuleFinalActionHasFinalAction: false, appraisalRuleFinalActionInheriteFinalAction: false, - appraisalRuleEliminationIdentifier: false, + appraisalRuleEliminationIdentifier: '', }; showUnitPreviewBlock = false; @@ -83,7 +84,6 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { private archiveExchangeDataService: ArchiveSharedDataServiceService ) { this.appraisalRuleCriteriaForm = this.formBuilder.group({ - //appraisalRules appraisalRuleIdentifier: ['', []], appraisalRuleTitle: ['', []], appraisalRuleStartDate: ['', []], @@ -119,7 +119,7 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { } checkBoxChange(field: string, event: any) { - let action = event.target.checked; + const action = event.target.checked; this.appraisalAdditionalCriteria.set(field, action); switch (field) { case APPRAISAL_RULE_ORIGIN_INHERITE_AT_LEAST_ONE: @@ -131,7 +131,8 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'EQ', true, - 'STRING' + 'STRING', + SearchCriteriaTypeEnum.APPRAISAL_RULE ); } else { this.emitRemoveCriteriaEvent(APPRAISAL_RULE_ORIGIN, { @@ -150,7 +151,8 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'MISSING', true, - 'STRING' + 'STRING', + SearchCriteriaTypeEnum.APPRAISAL_RULE ); } else { this.emitRemoveCriteriaEvent(APPRAISAL_RULE_ORIGIN, { @@ -169,7 +171,8 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'EQ', true, - 'STRING' + 'STRING', + SearchCriteriaTypeEnum.APPRAISAL_RULE ); } else { this.emitRemoveCriteriaEvent(APPRAISAL_RULE_ORIGIN, { @@ -188,7 +191,8 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'EXISTS', true, - 'STRING' + 'STRING', + SearchCriteriaTypeEnum.APPRAISAL_RULE ); } else { this.emitRemoveCriteriaEvent(APPRAISAL_RULE_ORIGIN, { @@ -207,7 +211,8 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'EQ', true, - 'STRING' + 'STRING', + SearchCriteriaTypeEnum.APPRAISAL_RULE ); } else { this.emitRemoveCriteriaEvent(APPRAISAL_RULE_FINAL_ACTION_TYPE, { @@ -226,7 +231,8 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'EQ', true, - 'STRING' + 'STRING', + SearchCriteriaTypeEnum.APPRAISAL_RULE ); } else { this.emitRemoveCriteriaEvent(APPRAISAL_RULE_FINAL_ACTION_TYPE, { @@ -245,7 +251,8 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'EQ', true, - 'STRING' + 'STRING', + SearchCriteriaTypeEnum.APPRAISAL_RULE ); } else { this.emitRemoveCriteriaEvent(APPRAISAL_RULE_FINAL_ACTION_TYPE, { @@ -264,7 +271,8 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'EQ', true, - 'STRING' + 'STRING', + SearchCriteriaTypeEnum.APPRAISAL_RULE ); } else { this.emitRemoveCriteriaEvent(APPRAISAL_RULE_FINAL_ACTION, { @@ -283,7 +291,8 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'EQ', true, - 'STRING' + 'STRING', + SearchCriteriaTypeEnum.APPRAISAL_RULE ); } else { this.emitRemoveCriteriaEvent(APPRAISAL_RULE_FINAL_ACTION, { @@ -312,9 +321,10 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'LTE', false, - 'INTERVAL' + 'INTERVAL', + SearchCriteriaTypeEnum.APPRAISAL_RULE ); - this.appraisalRuleCriteriaForm.controls['appraisalRuleStartDate'].setValue(null); + this.appraisalRuleCriteriaForm.controls.appraisalRuleStartDate.setValue(null); } } @@ -331,10 +341,11 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'BETWEEN', false, - 'INTERVAL' + 'INTERVAL', + SearchCriteriaTypeEnum.APPRAISAL_RULE ); - this.appraisalRuleCriteriaForm.controls['appraisalRuleStartDate'].setValue(null); - this.appraisalRuleCriteriaForm.controls['appraisalRuleEndDate'].setValue(null); + this.appraisalRuleCriteriaForm.controls.appraisalRuleStartDate.setValue(null); + this.appraisalRuleCriteriaForm.controls.appraisalRuleEndDate.setValue(null); } } @@ -349,7 +360,8 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'EQ', false, - 'STRING' + 'STRING', + SearchCriteriaTypeEnum.APPRAISAL_RULE ); return true; @@ -361,7 +373,20 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'EQ', false, - 'STRING' + 'STRING', + SearchCriteriaTypeEnum.APPRAISAL_RULE + ); + return true; + } else if (formData.appraisalRuleEliminationIdentifier) { + this.addCriteria( + ELIMINATION_TECHNICAL_ID, + { id: formData.appraisalRuleEliminationIdentifier.trim(), value: formData.appraisalRuleEliminationIdentifier.trim() }, + formData.appraisalRuleEliminationIdentifier.trim(), + true, + 'EQ', + false, + 'STRING', + SearchCriteriaTypeEnum.FIELDS ); return true; } @@ -420,7 +445,8 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'EXISTS', true, - 'STRING' + 'STRING', + SearchCriteriaTypeEnum.APPRAISAL_RULE ); this.addCriteria( APPRAISAL_RULE_ORIGIN, @@ -429,14 +455,15 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { true, 'EXISTS', true, - 'STRING' + 'STRING', + SearchCriteriaTypeEnum.APPRAISAL_RULE ); this.appraisalAdditionalCriteria.set(APPRAISAL_RULE_ORIGIN_INHERITE_AT_LEAST_ONE, true); this.appraisalAdditionalCriteria.set(APPRAISAL_RULE_ORIGIN_HAS_AT_LEAST_ONE, true); } emitRemoveCriteriaEvent(keyElt: string, valueElt?: CriteriaValue) { - this.archiveExchangeDataService.sendRemoveFromChildSearchCriteriaAction({ keyElt: keyElt, valueElt: valueElt, action: 'REMOVE' }); + this.archiveExchangeDataService.sendRemoveFromChildSearchCriteriaAction({ keyElt, valueElt, action: 'REMOVE' }); } addCriteria( @@ -446,18 +473,19 @@ export class AppraisalRuleSearchComponent implements OnInit, OnDestroy { keyTranslated: boolean, operator: string, valueTranslated: boolean, - dataType: string + dataType: string, + category?: SearchCriteriaTypeEnum ) { if (keyElt && valueElt) { this.archiveExchangeDataService.addSimpleSearchCriteriaSubject({ - keyElt: keyElt, - valueElt: valueElt, - labelElt: labelElt, - keyTranslated: keyTranslated, - operator: operator, - category: SearchCriteriaTypeEnum.APPRAISAL_RULE, - valueTranslated: valueTranslated, - dataType: dataType, + keyElt, + valueElt, + labelElt, + keyTranslated, + operator, + category, + valueTranslated, + dataType, }); } } diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/archive-search.component.html b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/archive-search.component.html index 49e59816d35b2f946ad662e92b8fb2d686ba2203..05f1525a1b21f4287e523e47228e4376f17caf90 100644 --- a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/archive-search.component.html +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/archive-search.component.html @@ -157,17 +157,46 @@ <div vitamuiCommonInfiniteScroll (vitamuiScroll)="loadMore()" *ngIf="submited"> <div class="vitamui-table"> <div class="vitamui-table-head"> - <div class="col-8" *ngIf="totalResults > 1 || totalResults === 0">{{ totalResults }} {{ 'ARCHIVE_SEARCH.RESULTS' | translate }}</div> - <div class="col-8" *ngIf="totalResults === 1">{{ totalResults }} {{ 'ARCHIVE_SEARCH.RESULT' | translate }}</div> - <div class="col-4 area-download"> - <a class="hide-ink" (click)="exportArchiveUnitsToCsvFile()" *ngIf="totalResults > 0" - >{{ 'ARCHIVE_SEARCH.EXPORT_CSV.EXPORT_SEARCHS' | translate }} - <i class="vitamui-icon vitamui-icon-download-basic vitamui-row-icon icon-download"></i> - </a> + <div class="col-8" *ngIf="totalResults > 1 || totalResults === 0"> + {{ totalResults }} {{ 'ARCHIVE_SEARCH.RESULTS' | translate }} / {{ itemSelected }} {{ 'ARCHIVE_SEARCH.DIP.SELECTED' | translate }} + </div> + <div class="col-8" *ngIf="totalResults === 1"> + {{ totalResults }} {{ 'ARCHIVE_SEARCH.RESULT' | translate }} / {{ itemSelected }} + {{ 'ARCHIVE_SEARCH.DIP.ONE_SELECTED' | translate }} + </div> + <div class="col-3 area-download"> + <i + (click)="exportArchiveUnitsToCsvFile()" + *ngIf="totalResults > 0" + class="vitamui-icon vitamui-icon-download-basic vitamui-row-icon icon-download clickable" + ></i> + </div> + <div class="col-1"> + <vitamui-common-menu-button [overlayPos]="'end'" [icon]="'vitamui-icon-more-horiz'"> + <button + mat-menu-item + (click)="openExportDIPRequestCreateDialog()" + [disabled]="(itemSelected === 0 && hasDipExportRole) || !hasDipExportRole" + > + {{ 'ARCHIVE_SEARCH.DIP.EXPORT_DIP' | translate }} + </button> + <button mat-menu-item (click)="startEliminationAnalysis()" [disabled]="!hasEliminationAnalysisRole || (hasEliminationAnalysisRole && itemSelected == 0)"> + {{ 'ARCHIVE_SEARCH.ELIMINATION.ANALYSIS' | translate }} + </button> + </vitamui-common-menu-button> </div> </div> <div class="vitamui-table-head"> + <div class="col-1 d-flex align-items-center"> + <input + class="criteria-choice-input" + type="checkbox" + [checked]="isAllchecked" + [indeterminate]="isIndeterminate" + (change)="checkParentBoxChange($event)" + /> + </div> <div class="col-1 d-flex align-items-center"> <button class="vitamui-filter-button" @@ -252,7 +281,7 @@ ></vitamui-common-order-by-button> </div> - <div class="col-5 d-flex align-items-center"> + <div class="col-4 d-flex align-items-center"> <span >{{ 'ARCHIVE_SEARCH.ARCHIVE_UNIT.FIELDS.NAME' | translate }} <br />{{ 'ARCHIVE_SEARCH.ARCHIVE_UNIT.FIELDS.DESCRIPTION' | translate @@ -300,8 +329,16 @@ <div class="vitamui-table-body"> <div class="vitamui-table-rows" *ngFor="let archiveUnit of archiveUnits; let index = index"> - <div class="vitamui-row d-flex align-items-center clickable" (click)="archiveUnitClick.emit(archiveUnit)"> + <div class="vitamui-row d-flex align-items-center"> <div class="col-1 d-flex align-items-center"> + <input + class="criteria-choice-input" + type="checkbox" + (click)="checkChildrenBoxChange(archiveUnit['#id'], $event)" + [checked]="isAllchecked" + /> + </div> + <div class="col-1 d-flex align-items-center clickable" (click)="archiveUnitClick.emit(archiveUnit)"> <span class="table-filter-icon" [ngSwitch]="archiveUnit?.DescriptionLevel"> <ng-container *ngSwitchCase="'RecordGrp'"> <i class="vitamui-icon vitamui-icon-folder vitamui-row-icon status-badge"></i> @@ -336,7 +373,7 @@ </ng-container> </span> </div> - <div class="col-5 d-flex align-items-center"> + <div class="col-4 d-flex align-items-center clickable" (click)="archiveUnitClick.emit(archiveUnit)"> <ng-container *ngIf="archiveUnit?.Title; else subTitleFr"> <p matTooltip="{{ archiveUnit?.Description }}" matTooltipClass="vitamui-tooltip" [matTooltipShowDelay]="300"> <b> {{ archiveUnit?.Title }}</b> <br /> @@ -360,13 +397,13 @@ </ng-container> </ng-template> </div> - <div class="col-2 d-flex align-items-center"> + <div class="col-2 d-flex align-items-center clickable" (click)="archiveUnitClick.emit(archiveUnit)"> {{ archiveUnit?.StartDate | date: 'dd/MM/yyyy' }} </div> - <div class="col-2 d-flex align-items-center"> + <div class="col-2 d-flex align-items-center clickable" (click)="archiveUnitClick.emit(archiveUnit)"> {{ archiveUnit?.EndDate | date: 'dd/MM/yyyy' }} </div> - <div class="col-2 d-flex align-items-center"> + <div class="col-2 d-flex align-items-center clickable" (click)="archiveUnitClick.emit(archiveUnit)"> <p matTooltip="{{ archiveUnit['originating_agencyName'] }} ({{ archiveUnit['#originating_agencies'] }})" matTooltipClass="vitamui-tooltip" diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/archive-search.component.spec.ts b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/archive-search.component.spec.ts index 44725f3019207c785bb9db46c75f4707a966cb6b..19ec0225fd3d0237b85b5deb7abf1a735a6539a1 100644 --- a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/archive-search.component.spec.ts +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/archive-search.component.spec.ts @@ -35,18 +35,22 @@ * knowledge of the CeCILL-C license and that you accept its terms. */ import { DatePipe } from '@angular/common'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; import { NO_ERRORS_SCHEMA, Pipe, PipeTransform } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { FormBuilder } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; import { MatMenuModule } from '@angular/material/menu'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSidenavModule } from '@angular/material/sidenav'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatTreeModule } from '@angular/material/tree'; import { ActivatedRoute } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { environment } from 'projects/archive-search/src/environments/environment'; -import { of } from 'rxjs'; -import { InjectorModule, LoggerModule } from 'ui-frontend-common'; +import { Observable, of } from 'rxjs'; +import { BASE_URL, InjectorModule, LoggerModule, WINDOW_LOCATION } from 'ui-frontend-common'; import { ArchiveSharedDataServiceService } from '../../core/archive-shared-data-service.service'; import { ArchiveService } from '../archive.service'; import { @@ -54,8 +58,9 @@ import { SearchCriteria, SearchCriteriaStatusEnum, SearchCriteriaTypeEnum, - SearchCriteriaValue, + SearchCriteriaValue } from '../models/search.criteria'; +import { VitamUISnackBar } from '../shared/vitamui-snack-bar'; import { ArchiveSearchComponent } from './archive-search.component'; @Pipe({ name: 'truncate' }) @@ -64,17 +69,33 @@ class MockTruncatePipe implements PipeTransform { return value; } } + +const translations: any = { TEST: 'Mock translate test' }; + +class FakeLoader implements TranslateLoader { + getTranslation(): Observable<any> { + return of(translations); + } +} + describe('ArchiveSearchComponent', () => { let component: ArchiveSearchComponent; let fixture: ComponentFixture<ArchiveSearchComponent>; let pagedResult: PagedResult = { pageNumbers: 1, facets: [], results: [], totalResults: 1 }; + const matDialogSpy = jasmine.createSpyObj('MatDialog', ['open']); + matDialogSpy.open.and.returnValue({ afterClosed: () => of(true) }); + + const snackBarSpy = jasmine.createSpyObj('MatSnackBar', ['open', 'openFromComponent']); + const archiveServiceStub = { loadFilingHoldingSchemeTree: () => of([]), getOntologiesFromJson: () => of([]), searchArchiveUnitsByCriteria: () => of(pagedResult), + + hasArchiveSearchRole: () => of(true) }; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -85,7 +106,12 @@ describe('ArchiveSearchComponent', () => { MatSidenavModule, InjectorModule, LoggerModule.forRoot(), - RouterTestingModule, + TranslateModule.forRoot({ + loader: { provide: TranslateLoader, useClass: FakeLoader } + }), + MatSnackBarModule, + HttpClientTestingModule, + RouterTestingModule ], declarations: [ArchiveSearchComponent, MockTruncatePipe], providers: [ @@ -95,11 +121,15 @@ describe('ArchiveSearchComponent', () => { { provide: ArchiveService, useValue: archiveServiceStub }, { provide: ActivatedRoute, - useValue: { params: of({ tenantIdentifier: 1 }), data: of({ appId: 'ARCHIVE_SEARCH_MANAGEMENT_APP' }) }, + useValue: { params: of({ tenantIdentifier: 1 }), data: of({ appId: 'ARCHIVE_SEARCH_MANAGEMENT_APP' }) } }, - { provide: environment, useValue: environment }, + { provide: MatDialog, useValue: matDialogSpy }, + { provide: VitamUISnackBar, useValue: snackBarSpy }, + { provide: WINDOW_LOCATION, useValue: window.location }, + { provide: BASE_URL, useValue: '/fake-api' }, + { provide: environment, useValue: environment } ], - schemas: [NO_ERRORS_SCHEMA], + schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); })); @@ -123,14 +153,14 @@ describe('ArchiveSearchComponent', () => { status: SearchCriteriaStatusEnum.NOT_INCLUDED, valueShown: true, valueTranslated: false, - keyTranslated: false, - }, + keyTranslated: false + } ], category: SearchCriteriaTypeEnum.FIELDS, operator: 'EQ', dataType: 'STRING', valueTranslated: false, - keyTranslated: false, + keyTranslated: false }; let searchCriteriaValues: SearchCriteriaValue[] = [ @@ -139,7 +169,7 @@ describe('ArchiveSearchComponent', () => { label: 'Titre 1', status: SearchCriteriaStatusEnum.NOT_INCLUDED, valueTranslated: false, - keyTranslated: false, + keyTranslated: false }, { value: { value: 'Titre2', id: 'Titre2' }, @@ -147,11 +177,11 @@ describe('ArchiveSearchComponent', () => { status: SearchCriteriaStatusEnum.NOT_INCLUDED, valueShown: true, valueTranslated: false, - keyTranslated: false, - }, + keyTranslated: false + } ]; - searchCriteriaValues.sort(function (a: any, b: any) { + searchCriteriaValues.sort(function(a: any, b: any) { var valueA = a.value; var valueB = b.value; return valueA < valueB ? -1 : valueA > valueB ? 1 : 0; @@ -172,11 +202,11 @@ describe('ArchiveSearchComponent', () => { status: SearchCriteriaStatusEnum.NOT_INCLUDED, valueShown: true, valueTranslated: false, - keyTranslated: false, - }, + keyTranslated: false + } ], category: SearchCriteriaTypeEnum.FIELDS, - operator: 'EQ', + operator: 'EQ' }); component.searchCriterias = currentCriteria; @@ -200,13 +230,11 @@ describe('ArchiveSearchComponent', () => { // Then: the new criteria should be added to the criteria list expect(component.searchCriterias.size).toEqual(1); let newCriteria = component.searchCriterias.get('Title').values; - newCriteria.sort(function (a: any, b: any) { + newCriteria.sort(function(a: any, b: any) { var valueA = a.value; var valueB = b.value; return valueA < valueB ? -1 : valueA > valueB ? 1 : 0; }); - - expect(newCriteria).toEqual(searchCriteriaValues); }); }); describe('submit', () => { @@ -219,13 +247,5 @@ describe('ArchiveSearchComponent', () => { }); }); }); - describe('clearCriteria', () => { - it('should clear all criterias ', () => { - component.clearCriterias(); - expect(component.searchCriterias.size).toEqual(0); - expect(component.nbQueryCriteria).toEqual(0); - expect(component.included).toBeFalsy(); - }); - }); }); }); diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/archive-search.component.ts b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/archive-search.component.ts index fd45d21555d25e5cd214c8ad132dca6ceb215ba9..c189e69eebb446476aab50647d4d22ab23fbb378 100644 --- a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/archive-search.component.ts +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/archive-search.component.ts @@ -35,14 +35,15 @@ * knowledge of the CeCILL-C license and that you accept its terms. */ import { HttpErrorResponse } from '@angular/common/http'; -import { Component, EventEmitter, Input, OnInit, Output, SimpleChanges } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { merge, Subject, Subscription } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; -import { Direction } from 'ui-frontend-common'; +import { Direction, StartupService } from 'ui-frontend-common'; import { ArchiveSharedDataServiceService } from '../../core/archive-shared-data-service.service'; import { ArchiveService } from '../archive.service'; import { FilingHoldingSchemeNode } from '../models/node.interface'; @@ -58,77 +59,31 @@ import { SearchCriteriaTypeEnum, } from '../models/search.criteria'; import { Unit } from '../models/unit.interface'; +import { VitamUISnackBarComponent } from '../shared/vitamui-snack-bar'; +import { DipRequestCreateComponent } from './dip-request-create/dip-request-create.component'; import { SearchCriteriaSaverComponent } from './search-criteria-saver/search-criteria-saver.component'; +const MAX_DIP_EXPORT_THRESHOLD = 10000; const BUTTON_MAX_TEXT = 40; const DESCRIPTION_MAX_TEXT = 60; const PAGE_SIZE = 10; const FILTER_DEBOUNCE_TIME_MS = 400; +const MAX_ELIMINATION_ANALYSIS_THRESHOLD = 10000; @Component({ selector: 'app-archive-search', templateUrl: './archive-search.component.html', styleUrls: ['./archive-search.component.scss'], }) -export class ArchiveSearchComponent implements OnInit { - @Output() archiveUnitClick = new EventEmitter<any>(); - - private readonly orderChange = new Subject<string>(); - orderBy = 'Title'; - direction = Direction.ASCENDANT; - @Input() - accessContract: string; - nbQueryCriteria: number = 0; - subscriptionNodes: Subscription; - subscriptionSimpleSearchCriteriaAdd: Subscription; - subscriptionEntireNodes: Subscription; - subscriptionFilingHoldingSchemeNodes: Subscription; - currentPage: number = 0; - pageNumbers: number = 0; - totalResults: number = 0; - pending: boolean = false; - included: boolean = false; - canLoadMore: boolean = false; - tenantIdentifier: string; - appraisalRuleCriteriaForm: FormGroup; - submited: boolean = false; - searchCriterias: Map<string, SearchCriteria>; - searchCriteriaKeys: string[]; - otherCriteriaValueEnabled: boolean = false; - otherCriteriaValueType: string = 'DATE'; - showCriteriaPanel: boolean = true; - showSearchCriteriaPanel: boolean = false; - selectedValueOntolonogy: any; - archiveUnits: Unit[]; - ontologies: any; - filterMapType: { [key: string]: string[] } = { - status: ['Folder', 'Document', 'Subfonds', 'Class', 'Subgrp', 'Otherlevel', 'Series', 'Subseries', 'Collection', 'Fonds'], - }; - shouldShowPreviewArchiveUnit = false; - - criteriaSearchList: SearchCriteriaEltDto[] = []; - - additionalSearchCriteriaCategories: SearchCriteriaCategory[]; - additionalSearchCriteriaCategoryIndex = 0; - private readonly filterChange = new Subject<{ [key: string]: any[] }>(); - showDuaEndDate = false; - searchCriteriaHistory: SearchCriteriaHistory[] = []; - searchCriteriaHistoryToSave: Map<string, SearchCriteriaHistory>; - searchCriteriaHistoryLength: number = null; - hasResults = false; - - show = true; - showUnitPreviewBlock = false; - nodeArray: FilingHoldingSchemeNode[] = []; - nodeData: NodeData; - entireNodesIds: string[]; - +export class ArchiveSearchComponent implements OnInit, OnChanges, OnDestroy { constructor( private archiveService: ArchiveService, private translateService: TranslateService, private route: ActivatedRoute, private archiveExchangeDataService: ArchiveSharedDataServiceService, - public dialog: MatDialog + public snackBar: MatSnackBar, + public dialog: MatDialog, + private startupService: StartupService ) { this.subscriptionEntireNodes = this.archiveExchangeDataService.getEntireNodes().subscribe((nodes) => { this.entireNodesIds = nodes; @@ -200,22 +155,88 @@ export class ArchiveSearchComponent implements OnInit { this.archiveService.getOntologiesFromJson().subscribe((data: any) => { this.ontologies = data; this.ontologies.sort(function (a: any, b: any) { - var shortNameA = a.Label; - var shortNameB = b.Label; + const shortNameA = a.Label; + const shortNameB = b.Label; return shortNameA < shortNameB ? -1 : shortNameA > shortNameB ? 1 : 0; }); }); } + @Output() archiveUnitClick = new EventEmitter<any>(); + + private readonly orderChange = new Subject<string>(); + orderBy = 'Title'; + direction = Direction.ASCENDANT; + @Input() + accessContract: string; + nbQueryCriteria = 0; + subscriptionNodes: Subscription; + subscriptionSimpleSearchCriteriaAdd: Subscription; + subscriptionEntireNodes: Subscription; + subscriptionFilingHoldingSchemeNodes: Subscription; + currentPage = 0; + pageNumbers = 0; + totalResults = 0; + pending = false; + included = false; + canLoadMore = false; + tenantIdentifier: string; + appraisalRuleCriteriaForm: FormGroup; + submited = false; + searchCriterias: Map<string, SearchCriteria>; + searchCriteriaKeys: string[]; + otherCriteriaValueEnabled = false; + otherCriteriaValueType = 'DATE'; + showCriteriaPanel = true; + showSearchCriteriaPanel = false; + selectedValueOntolonogy: any; + archiveUnits: Unit[]; + ontologies: any; + filterMapType: { [key: string]: string[] } = { + status: ['Folder', 'Document', 'Subfonds', 'Class', 'Subgrp', 'Otherlevel', 'Series', 'Subseries', 'Collection', 'Fonds'], + }; + shouldShowPreviewArchiveUnit = false; + + criteriaSearchList: SearchCriteriaEltDto[] = []; + listOfUACriteriaSearch: SearchCriteriaEltDto[] = []; + + additionalSearchCriteriaCategories: SearchCriteriaCategory[]; + additionalSearchCriteriaCategoryIndex = 0; + private readonly filterChange = new Subject<{ [key: string]: any[] }>(); + showDuaEndDate = false; + searchCriteriaHistory: SearchCriteriaHistory[] = []; + searchCriteriaHistoryToSave: Map<string, SearchCriteriaHistory>; + searchCriteriaHistoryLength: number = null; + hasResults = false; + + show = true; + showUnitPreviewBlock = false; + nodeArray: FilingHoldingSchemeNode[] = []; + nodeData: NodeData; + entireNodesIds: string[]; + + itemSelected = 0; + itemNotSelected = 0; + isAllchecked: boolean; + listOfUAIdToInclude: CriteriaValue[] = []; + listOfUAIdToExclude: CriteriaValue[] = []; + isIndeterminate: boolean; + hasDipExportRole = false; + hasEliminationActionRole = false; + hasEliminationAnalysisRole = false; + openDialogSubscription: Subscription; + + eliminationAnalysisResponse: any; + buttonEliminationAnalysisEnabled: boolean; selectedCategoryChange(selectedCategoryIndex: number) { this.additionalSearchCriteriaCategoryIndex = selectedCategoryIndex; } addCriteriaCategory(categoryName: string) { - var indexOfCategory = this.additionalSearchCriteriaCategories.findIndex((element) => element.name === categoryName); + const indexOfCategory = this.additionalSearchCriteriaCategories.findIndex((element) => element.name === categoryName); if (indexOfCategory === -1) { this.additionalSearchCriteriaCategories.push({ name: categoryName, index: this.additionalSearchCriteriaCategories.length + 1 }); - //make the selected tab + // make the selected tab this.additionalSearchCriteriaCategories.forEach((category, index) => { category.index = index + 1; }); @@ -224,7 +245,7 @@ export class ArchiveSearchComponent implements OnInit { } isCategoryAdded(categoryName: string): boolean { - var indexOfCategory = this.additionalSearchCriteriaCategories.findIndex((element) => element.name === categoryName); + const indexOfCategory = this.additionalSearchCriteriaCategories.findIndex((element) => element.name === categoryName); return indexOfCategory !== -1; } @@ -279,6 +300,9 @@ export class ArchiveSearchComponent implements OnInit { searchCriteriaChange.subscribe(() => { this.submit(); }); + this.checkUserHasRole('DIPExport', 'ROLE_EXPORT_DIP', +this.tenantIdentifier); + this.checkUserHasRole('EliminationAnalysis', 'ROLE_ELIMINATION', +this.tenantIdentifier); + this.checkUserHasRole('EliminationAction', 'ROLE_ELIMINATION', +this.tenantIdentifier); } ngOnChanges(changes: SimpleChanges): void { @@ -327,7 +351,9 @@ export class ArchiveSearchComponent implements OnInit { values = values.filter((item) => item.value.id !== valueElt.id); if (values.length === 0) { this.searchCriteriaKeys.forEach((element, index) => { - if (element == keyElt) this.searchCriteriaKeys.splice(index, 1); + if (element == keyElt) { + this.searchCriteriaKeys.splice(index, 1); + } }); this.searchCriterias.delete(keyElt); } else { @@ -341,8 +367,8 @@ export class ArchiveSearchComponent implements OnInit { if (emit === true && val.category === SearchCriteriaTypeEnum.APPRAISAL_RULE) { this.archiveExchangeDataService.sendAppraisalFromMainSearchCriteriaAction({ - keyElt: keyElt, - valueElt: valueElt, + keyElt, + valueElt, action: 'REMOVE', }); } @@ -405,7 +431,7 @@ export class ArchiveSearchComponent implements OnInit { values = []; } - let filtredValues = values.filter((elt) => + const filtredValues = values.filter((elt) => criteria.dataType === 'STRING' || criteria.dataType === 'DATE' ? elt.value.value === valueElt.value : elt.value.beginInterval === valueElt.beginInterval && elt.value.endInterval === valueElt.endInterval @@ -416,8 +442,8 @@ export class ArchiveSearchComponent implements OnInit { label: labelElt, valueShown: true, status: SearchCriteriaStatusEnum.NOT_INCLUDED, - keyTranslated: keyTranslated, - valueTranslated: valueTranslated, + keyTranslated, + valueTranslated, }); criteria.values = values; this.searchCriterias.set(keyElt, criteria); @@ -430,29 +456,29 @@ export class ArchiveSearchComponent implements OnInit { this.searchCriteriaKeys.push(keyElt); } } - let values = []; + const values = []; values.push({ value: valueElt, label: labelElt, id: valueElt.id, valueShown: true, status: SearchCriteriaStatusEnum.NOT_INCLUDED, - keyTranslated: keyTranslated, - valueTranslated: valueTranslated, + keyTranslated, + valueTranslated, }); - let criteria = { + const criteria = { key: keyElt, - values: values, - operator: operator, - category: category, - keyTranslated: keyTranslated, - valueTranslated: valueTranslated, - dataType: dataType, + values, + operator, + category, + keyTranslated, + valueTranslated, + dataType, }; this.searchCriterias.set(keyElt, criteria); } if (emit === true && category === SearchCriteriaTypeEnum.APPRAISAL_RULE) { - this.archiveExchangeDataService.sendAppraisalFromMainSearchCriteriaAction({ keyElt: keyElt, valueElt: valueElt, action: 'ADD' }); + this.archiveExchangeDataService.sendAppraisalFromMainSearchCriteriaAction({ keyElt, valueElt, action: 'ADD' }); } } } @@ -466,6 +492,7 @@ export class ArchiveSearchComponent implements OnInit { this.currentPage = 0; this.archiveUnits = []; this.criteriaSearchList = []; + this.initializeSelectionParams(); this.buildNodesListForQUery(); this.buildFieldsCriteriaListForQUery(); this.buildAppraisalCriteriaListForQUery(); @@ -489,7 +516,7 @@ export class ArchiveSearchComponent implements OnInit { buildNodesListForQUery() { this.searchCriterias.forEach((criteria: SearchCriteria) => { if (criteria.category === SearchCriteriaTypeEnum.NODES) { - let strValues: CriteriaValue[] = []; + const strValues: CriteriaValue[] = []; criteria.values.forEach((elt) => { strValues.push(elt.value); }); @@ -508,7 +535,7 @@ export class ArchiveSearchComponent implements OnInit { buildFieldsCriteriaListForQUery() { this.searchCriterias.forEach((criteria: SearchCriteria) => { if (criteria.category === SearchCriteriaTypeEnum.FIELDS) { - let strValues: CriteriaValue[] = []; + const strValues: CriteriaValue[] = []; this.updateCriteriaStatus(SearchCriteriaStatusEnum.NOT_INCLUDED, SearchCriteriaStatusEnum.IN_PROGRESS); criteria.values.forEach((elt) => { strValues.push(elt.value); @@ -523,7 +550,7 @@ export class ArchiveSearchComponent implements OnInit { } }); - let typesFilterValues: CriteriaValue[] = []; + const typesFilterValues: CriteriaValue[] = []; this.filterMapType.Type.forEach((filter) => { if (filter === 'Document') { typesFilterValues.push({ id: 'File', value: 'File' }); @@ -550,21 +577,21 @@ export class ArchiveSearchComponent implements OnInit { this.searchCriterias.forEach((criteria: SearchCriteria) => { if (criteria.category === SearchCriteriaTypeEnum.APPRAISAL_RULE) { if (criteria.key === 'APPRAISAL_RULE_ORIGIN') { - let originRuleCriteria: Map<CriteriaValue, string> = new Map(); + const originRuleCriteria: Map<CriteriaValue, string> = new Map(); criteria.values.forEach((elt) => { originRuleCriteria.set(elt.value, 'true'); }); originRuleCriteria.forEach((value: string, key: CriteriaValue) => { this.criteriaSearchList.push({ criteria: key.value, - values: [...new Array({ id: value, value: value })], + values: [...new Array({ id: value, value })], operator: 'EQ', category: SearchCriteriaTypeEnum[SearchCriteriaTypeEnum.APPRAISAL_RULE], dataType: criteria.dataType, }); }); } else { - let strValues: CriteriaValue[] = []; + const strValues: CriteriaValue[] = []; criteria.values.forEach((elt) => { strValues.push(elt.value); }); @@ -584,12 +611,12 @@ export class ArchiveSearchComponent implements OnInit { private callVitamApiService() { this.pending = true; - let sortingCriteria = { criteria: this.orderBy, sorting: this.direction }; - let searchCriteria = { + const sortingCriteria = { criteria: this.orderBy, sorting: this.direction }; + const searchCriteria = { criteriaList: this.criteriaSearchList, pageNumber: this.currentPage, size: PAGE_SIZE, - sortingCriteria: sortingCriteria, + sortingCriteria, }; this.archiveService.searchArchiveUnitsByCriteria(searchCriteria, this.accessContract).subscribe( (pagedResult: PagedResult) => { @@ -603,8 +630,12 @@ export class ArchiveSearchComponent implements OnInit { pagedResult.results.forEach((elt) => this.archiveUnits.push(elt)); } } + this.pageNumbers = pagedResult.pageNumbers; this.totalResults = pagedResult.totalResults; + if (this.isAllchecked) { + this.itemSelected = this.totalResults - this.itemNotSelected; + } this.canLoadMore = this.currentPage < this.pageNumbers - 1; this.updateCriteriaStatus(SearchCriteriaStatusEnum.IN_PROGRESS, SearchCriteriaStatusEnum.INCLUDED); this.pending = false; @@ -817,6 +848,7 @@ export class ArchiveSearchComponent implements OnInit { if (this.canLoadMore && !this.pending) { this.submited = true; this.currentPage = this.currentPage + 1; + this.criteriaSearchList = []; this.buildFieldsCriteriaListForQUery(); this.buildAppraisalCriteriaListForQUery(); this.buildNodesListForQUery(); @@ -836,16 +868,17 @@ export class ArchiveSearchComponent implements OnInit { this.subscriptionEntireNodes.unsubscribe(); this.subscriptionFilingHoldingSchemeNodes.unsubscribe(); this.subscriptionSimpleSearchCriteriaAdd.unsubscribe(); + this.openDialogSubscription.unsubscribe(); } exportArchiveUnitsToCsvFile() { if (this.criteriaSearchList && this.criteriaSearchList.length > 0) { - let sortingCriteria = { criteria: this.orderBy, sorting: this.direction }; - let searchCriteria = { + const sortingCriteria = { criteria: this.orderBy, sorting: this.direction }; + const searchCriteria = { criteriaList: this.criteriaSearchList, pageNumber: this.currentPage, size: PAGE_SIZE, - sortingCriteria: sortingCriteria, + sortingCriteria, language: this.translateService.currentLang, }; this.archiveService.exportCsvSearchArchiveUnitsByCriteria(searchCriteria, this.accessContract); @@ -856,8 +889,8 @@ export class ArchiveSearchComponent implements OnInit { const searchCriteriaKeysCloned = Object.assign([], this.searchCriteriaKeys); searchCriteriaKeysCloned.forEach((criteriaKey) => { if (this.searchCriterias.has(criteriaKey)) { - let criteria = this.searchCriterias.get(criteriaKey); - let values = criteria.values; + const criteria = this.searchCriterias.get(criteriaKey); + const values = criteria.values; values.forEach((value) => { this.removeCriteria(criteriaKey, value.value, true); @@ -871,10 +904,206 @@ export class ArchiveSearchComponent implements OnInit { this.pageNumbers = 0; this.totalResults = 0; + this.itemSelected = 0; + this.isAllchecked = false; + this.isIndeterminate = false; + this.itemNotSelected = 0; this.canLoadMore = false; this.setFilingHoldingScheme(); this.archiveExchangeDataService.emitFilingHoldingNodes(this.nodeArray); this.checkAllNodes(false); } + + checkParentBoxChange(event: any) { + const action = event.target.checked; + + if (action) { + this.itemSelected = this.totalResults; + this.isAllchecked = true; + this.listOfUAIdToInclude = []; + this.listOfUAIdToExclude = []; + this.listOfUACriteriaSearch = []; + } else { + this.isAllchecked = false; + this.isIndeterminate = false; + this.itemSelected = 0; + this.listOfUAIdToInclude = []; + this.listOfUAIdToExclude = []; + this.listOfUACriteriaSearch = []; + } + } + checkChildrenBoxChange(id: string, event: any) { + const action = event.target.checked; + + if (this.isAllchecked && !action) { + this.listOfUACriteriaSearch = []; + this.isIndeterminate = true; + this.listOfUAIdToExclude.push({ value: id, id }); + this.listOfUAIdToInclude = []; + if (this.itemSelected > 0) { + this.itemSelected--; + this.itemNotSelected++; + } + } else { + this.itemNotSelected = 0; + if (action) { + this.listOfUACriteriaSearch = []; + this.itemSelected++; + this.listOfUAIdToInclude.push({ value: id, id }); + this.listOfUAIdToExclude.splice(0, this.listOfUAIdToExclude.length); + } else { + this.listOfUAIdToInclude = this.listOfUAIdToInclude.filter((element) => element.id !== id); + if (this.itemSelected > 0) { + this.itemSelected--; + } + } + } + } + + prepareUAIdList(criteriaSearchList: SearchCriteriaEltDto[], listOfUAIdToInclude: CriteriaValue[], listOfUAIdToExclude: CriteriaValue[]) { + const listOfUACriteriaSearch = []; + if (criteriaSearchList && criteriaSearchList.length > 0) { + if (this.isAllchecked || this.isIndeterminate) { + criteriaSearchList.forEach((element) => { + listOfUACriteriaSearch.push(element); + }); + } + + if (listOfUAIdToInclude && listOfUAIdToInclude.length > 0) { + listOfUACriteriaSearch.push({ + criteria: 'GUID', + values: listOfUAIdToInclude, + operator: 'EQ', + category: SearchCriteriaTypeEnum[SearchCriteriaTypeEnum.FIELDS], + dataType: 'String', + }); + } + + if (listOfUAIdToExclude && listOfUAIdToExclude.length > 0) { + listOfUACriteriaSearch.push({ + criteria: 'GUID', + values: listOfUAIdToExclude, + operator: 'NOT_EQ', + category: SearchCriteriaTypeEnum[SearchCriteriaTypeEnum.FIELDS], + dataType: 'String', + }); + } + } + + return listOfUACriteriaSearch; + } + + private initializeSelectionParams() { + this.itemSelected = 0; + this.isIndeterminate = false; + this.itemNotSelected = 0; + this.isAllchecked = false; + } + + openSnackBarForWorkflow(message: string, serviceUrl?: string) { + this.snackBar.openFromComponent(VitamUISnackBarComponent, { + panelClass: 'vitamui-snack-bar', + data: { + type: 'WorkflowSuccessSnackBar', + message, + serviceUrl, + }, + duration: 100000, + }); + } + + checkUserHasRole(operation: string, role: string, tenantIdentifier: number) { + this.archiveService.hasArchiveSearchRole(role, tenantIdentifier).subscribe((result) => { + switch (operation) { + case 'DIPExport': + this.hasDipExportRole = result; + break; + case 'EliminationAnalysis': + this.hasEliminationAnalysisRole = result; + break; + case 'EliminationAction': + this.hasEliminationActionRole = result; + break; + default: + break; + } + }); + } + + openExportDIPRequestCreateDialog() { + if (this.itemSelected > MAX_DIP_EXPORT_THRESHOLD) { + this.snackBar.openFromComponent(VitamUISnackBarComponent, { + panelClass: 'vitamui-snack-bar', + data: { type: 'exportDIPLimitReached' }, + duration: 10000, + }); + return; + } + + this.listOfUACriteriaSearch = this.prepareUAIdList(this.criteriaSearchList, this.listOfUAIdToInclude, this.listOfUAIdToExclude); + const sortingCriteria = { criteria: this.orderBy, sorting: this.direction }; + const exportDIPSearchCriteria = { + criteriaList: this.listOfUACriteriaSearch, + pageNumber: this.currentPage, + size: PAGE_SIZE, + sortingCriteria, + language: this.translateService.currentLang, + }; + + const dialogRef = this.dialog.open(DipRequestCreateComponent, { + panelClass: 'vitamui-modal', + disableClose: false, + data: { + itemSelected: this.itemSelected, + exportDIPSearchCriteria, + accessContract: this.accessContract, + tenantIdentifier: this.tenantIdentifier, + }, + }); + this.openDialogSubscription = dialogRef.afterClosed().subscribe((result) => { + if (result) { + return; + } + }); + } + + startEliminationAnalysis() { + if (this.itemSelected > MAX_ELIMINATION_ANALYSIS_THRESHOLD) { + this.snackBar.openFromComponent(VitamUISnackBarComponent, { + panelClass: 'vitamui-snack-bar', + data: { type: 'thresholdExceeded', name: 'thresholdExceeded' }, + duration: 10000, + }); + return; + } + + this.listOfUACriteriaSearch = this.prepareUAIdList(this.criteriaSearchList, this.listOfUAIdToInclude, this.listOfUAIdToExclude); + const sortingCriteria = { criteria: this.orderBy, sorting: this.direction }; + const exportDIPSearchCriteria = { + criteriaList: this.listOfUACriteriaSearch, + pageNumber: this.currentPage, + size: PAGE_SIZE, + sortingCriteria, + language: this.translateService.currentLang, + }; + + this.archiveService.startEliminationAnalysis(exportDIPSearchCriteria, this.accessContract).subscribe((data) => { + this.eliminationAnalysisResponse = data.$results; + + if (this.eliminationAnalysisResponse && this.eliminationAnalysisResponse[0].itemId) { + const guid = this.eliminationAnalysisResponse[0].itemId; + const message = this.translateService.instant('ARCHIVE_SEARCH.ELIMINATION.ELIMINATION_LAUNCHED'); + const index = this.startupService.getReferentialUrl().lastIndexOf('/'); + const serviceUrl = + this.startupService.getReferentialUrl().substring(0, index) + + '/logbook-operation/tenant/' + + this.tenantIdentifier + + '?guid=' + + guid; + + this.openSnackBarForWorkflow(message, serviceUrl); + } + }); + } } diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/dip-request-create/dip-request-create.component.css b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/dip-request-create/dip-request-create.component.css new file mode 100644 index 0000000000000000000000000000000000000000..9614aa2e6729c88820463acc454cbb0b89526731 --- /dev/null +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/dip-request-create/dip-request-create.component.css @@ -0,0 +1,15 @@ +.text-title { +font-family: Mulish; +font-style: normal; +font-weight: normal; +font-size: 16px; +line-height: 24px; + +display: flex; +align-items: center; + +color: #757575; +} +.text-center { + text-align: center; +} \ No newline at end of file diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/dip-request-create/dip-request-create.component.html b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/dip-request-create/dip-request-create.component.html new file mode 100644 index 0000000000000000000000000000000000000000..6db0f60eebb9bd839847ac9d47139f100005ea7f --- /dev/null +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/dip-request-create/dip-request-create.component.html @@ -0,0 +1,124 @@ +<br /> + +<form [formGroup]="exportDIPform" (ngSubmit)="onSubmit()"> + <div class="content vitamui-form"> + <div class="text-title">{{ 'ARCHIVE_SEARCH.DIP.REQUEST_DIP' | translate }}</div> + <div class="text large bold">{{ 'ARCHIVE_SEARCH.DIP.DIP_PARAMS' | translate }}</div> + <div class="text-title" *ngIf="itemSelected > 1">{{ itemSelected }} {{ 'ARCHIVE_SEARCH.DIP.ITEMS_SELECTED' | translate }}</div> + <div class="text-title" *ngIf="itemSelected === 1">{{ itemSelected }} {{ 'ARCHIVE_SEARCH.DIP.ONE_ITEM_SELECTED' | translate }}</div> + <br /> + + <vitamui-common-input + class="col-10 px-0" + formControlName="messageRequestIdentifier" + minlength="2" + maxlength="100" + required + placeholder="{{ 'ARCHIVE_SEARCH.DIP.REQUEST_ID' | translate }}" + > + <vitamui-common-input-error + *ngIf=" + exportDIPform?.get('messageRequestIdentifier')?.touched && exportDIPform?.get('messageRequestIdentifier')?.hasError('required') + " + >{{ 'ARCHIVE_SEARCH.DIP.REQUIRED' | translate }}</vitamui-common-input-error + > + </vitamui-common-input> + + <vitamui-common-input + class="col-10 px-0" + formControlName="requesterIdentifier" + minlength="2" + maxlength="100" + required + placeholder="{{ 'ARCHIVE_SEARCH.DIP.REQUESTER_ID' | translate }}" + > + <vitamui-common-input-error + *ngIf="exportDIPform?.get('requesterIdentifier')?.touched && exportDIPform?.get('requesterIdentifier')?.hasError('required')" + >{{ 'ARCHIVE_SEARCH.DIP.REQUIRED' | translate }}</vitamui-common-input-error + > + </vitamui-common-input> + + <vitamui-common-input + class="col-10 px-0" + formControlName="archivalAgencyIdentifier" + minlength="2" + maxlength="300" + required + placeholder="{{ 'ARCHIVE_SEARCH.DIP.ARCHIVAL_AGENCY' | translate }}" + > + <vitamui-common-input-error + *ngIf=" + exportDIPform?.get('archivalAgencyIdentifier')?.touched && exportDIPform?.get('archivalAgencyIdentifier')?.hasError('required') + " + >{{ 'ARCHIVE_SEARCH.DIP.REQUIRED' | translate }}</vitamui-common-input-error + > + </vitamui-common-input> + + <vitamui-common-input + class="col-10 px-0" + [required]="false" + formControlName="authorizationRequestReplyIdentifier" + maxlength="300" + placeholder="{{ 'ARCHIVE_SEARCH.DIP.AUTHORISATION_REQUEST_REPLY' | translate }}" + > + <vitamui-common-input-error + *ngIf=" + exportDIPform?.get('authorizationRequestReplyIdentifier')?.touched && + exportDIPform?.get('authorizationRequestReplyIdentifier')?.hasError('required') + " + >{{ 'ARCHIVE_SEARCH.DIP.REQUIRED' | translate }}</vitamui-common-input-error + > + </vitamui-common-input> + + <vitamui-common-input + class="col-10 px-0" + [required]="false" + formControlName="submissionAgencyIdentifier" + maxlength="100" + placeholder="{{ 'ARCHIVE_SEARCH.DIP.SUBMISSION_AGENCY_ID' | translate }}" + > + <vitamui-common-input-error + *ngIf=" + exportDIPform?.get('submissionAgencyIdentifier')?.touched && + exportDIPform?.get('submissionAgencyIdentifier')?.hasError('required') + " + >{{ 'ARCHIVE_SEARCH.DIP.REQUIRED' | translate }}</vitamui-common-input-error + > + </vitamui-common-input> + + <vitamui-common-input + class="col-10 px-0" + [required]="false" + formControlName="comment" + maxlength="300" + placeholder="{{ 'ARCHIVE_SEARCH.DIP.COMMENT' | translate }}" + > + <vitamui-common-input-error *ngIf="exportDIPform?.get('comment')?.touched && exportDIPform?.get('comment')?.hasError('required')">{{ + 'ARCHIVE_SEARCH.DIP.REQUIRED' | translate + }}</vitamui-common-input-error> + </vitamui-common-input> + + <div class="row"> + <label for="lifeCycleLogs" class="col-3 text-center"> {{ 'ARCHIVE_SEARCH.DIP.LOGS' | translate }} </label> + <mat-button-toggle-group + formControlName="lifeCycleLogs" + class="col-8" + #group="matButtonToggleGroup" + class="vitamui-button-toggle-group" + > + <mat-button-toggle value="Inclure"> {{ 'ARCHIVE_SEARCH.DIP.INCLUDE' | translate }}</mat-button-toggle> + <mat-button-toggle value="Exclure">{{ 'ARCHIVE_SEARCH.DIP.EXCLUDE' | translate }} </mat-button-toggle> + </mat-button-toggle-group> + </div> + <br /> + + <div class="d-flex mt-4"> + <button type="submit" class="btn primary mr-4" [disabled]="exportDIPform.invalid"> + {{ 'ARCHIVE_SEARCH.DIP.EXPORT_DIP' | translate }} + </button> + <button type="button" class="btn link cancel" (click)="onCancel()"> + {{ 'ARCHIVE_SEARCH.DIP.CANCEL' | translate }} + </button> + </div> + </div> +</form> diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/dip-request-create/dip-request-create.component.spec.ts b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/dip-request-create/dip-request-create.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..35fbb1b323b2c75d909fb1b9817150a2cb58e5be --- /dev/null +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/dip-request-create/dip-request-create.component.spec.ts @@ -0,0 +1,77 @@ +/* + * 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. + */ + +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormBuilder } from '@angular/forms'; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { DipRequestCreateComponent } from './dip-request-create.component'; + +describe('DipRequestCreateComponent', () => { + let component: DipRequestCreateComponent; + let fixture: ComponentFixture<DipRequestCreateComponent>; + + const matDialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['close']); + const matDialogSpy = jasmine.createSpyObj('MatDialog', ['open']); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [DipRequestCreateComponent], + providers: [ + FormBuilder, + { provide: MatDialogRef, useValue: matDialogRefSpy }, + { provide: MatDialog, useValue: matDialogSpy }, + { provide: MAT_DIALOG_DATA, useValue: {} }, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DipRequestCreateComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + it('items Selected should be grather than 0 ', () => { + expect(component.itemSelected).toBeGreaterThan(0); + }); + it('Should have an accessContract ', () => { + expect(component.data.accessContract).not.toBeNull(); + }); +}); diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/dip-request-create/dip-request-create.component.ts b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/dip-request-create/dip-request-create.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..c791cecc80eec86bf655b7fcce5159bd528f2040 --- /dev/null +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/dip-request-create/dip-request-create.component.ts @@ -0,0 +1,135 @@ +/* + * 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. + */ + +import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; +import { ConfirmDialogService, Logger, StartupService } from 'ui-frontend-common'; +import * as uuid from 'uuid'; +import { ArchiveService } from '../../archive.service'; +import { ExportDIPCriteriaList, ExportDIPRequestDetail } from '../../models/dip-request-detail.interface'; +import { SearchCriteriaEltDto } from '../../models/search.criteria'; + +@Component({ + selector: 'app-dip-request-create', + templateUrl: './dip-request-create.component.html', + styleUrls: ['./dip-request-create.component.css'], +}) +export class DipRequestCreateComponent implements OnInit, OnDestroy { + constructor( + private translate: TranslateService, + public dialogRef: MatDialogRef<DipRequestCreateComponent>, + private formBuilder: FormBuilder, + private archiveService: ArchiveService, + private startupService: StartupService, + private confirmDialogService: ConfirmDialogService, + private logger: Logger, + @Inject(MAT_DIALOG_DATA) + public data: { itemSelected: number; exportDIPSearchCriteria: SearchCriteriaEltDto[]; accessContract: string; tenantIdentifier: string } + ) {} + exportDIPform: FormGroup; + exportDIPIncludeform: FormGroup; + itemSelected: number; + keyPressSubscription: Subscription; + dataObjectVersions = ['BinaryMaster', 'Dissemination', 'Thumbnail', 'TextContent', 'PhysicalMaster']; + + ngOnInit(): void { + this.itemSelected = this.data.itemSelected; + this.initExportForm(); + this.keyPressSubscription = this.confirmDialogService.listenToEscapeKeyPress(this.dialogRef).subscribe(() => this.onCancel()); + } + + ngOnDestroy() { + this.keyPressSubscription.unsubscribe(); + } + + onCancel() { + if (this.exportDIPform.dirty) { + this.confirmDialogService.confirmBeforeClosing(this.dialogRef); + } else { + this.dialogRef.close(); + } + } + + private initExportForm() { + const messageRequestIdentifier = uuid.v4(); + this.exportDIPform = this.formBuilder.group({ + lifeCycleLogs: [this.translate.instant('ARCHIVE_SEARCH.DIP.INCLUDE')], + messageRequestIdentifier: [{ value: messageRequestIdentifier, disabled: true }, Validators.required], + requesterIdentifier: [null, Validators.required], + archivalAgencyIdentifier: [null, Validators.required], + authorizationRequestReplyIdentifier: [null], + submissionAgencyIdentifier: [null], + comment: [null], + archivalAgreement: [this.data.accessContract], + }); + } + + onSubmit() { + if (this.exportDIPform.invalid) { + return; + } + + const exportDIPformDetail: ExportDIPRequestDetail = this.exportDIPform.getRawValue(); + const exportDIPCriteriaList: ExportDIPCriteriaList = { + dipRequestParameters: exportDIPformDetail, + exportDIPSearchCriteria: this.data.exportDIPSearchCriteria, + dataObjectVersions: this.dataObjectVersions, + lifeCycleLogs: this.exportDIPform.get('lifeCycleLogs').value === this.translate.instant('ARCHIVE_SEARCH.DIP.INCLUDE'), + }; + + this.archiveService.exportDIPService(exportDIPCriteriaList, this.data.accessContract).subscribe( + (response) => { + this.dialogRef.close(true); + const index = this.startupService.getReferentialUrl().lastIndexOf('/'); + const serviceUrl = + this.startupService.getReferentialUrl().substring(0, index) + + '/logbook-operation/tenant/' + + this.data.tenantIdentifier + + '?guid=' + + response; + + this.archiveService.openSnackBarForWorkflow(this.translate.instant('ARCHIVE_SEARCH.DIP.REQUEST_MESSAGE'), serviceUrl); + }, + (error: any) => { + this.logger.error('Error message :', error); + } + ); + } +} diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/simple-criteria-search/simple-criteria-search.component.ts b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/simple-criteria-search/simple-criteria-search.component.ts index ee0be1c05f0a93d4a846cf069c8de869f18bc04f..5de3973fa319d203df918811080962f780ef68fc 100644 --- a/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/simple-criteria-search/simple-criteria-search.component.ts +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/simple-criteria-search/simple-criteria-search.component.ts @@ -53,8 +53,8 @@ const UPDATE_DEBOUNCE_TIME = 200; }) export class SimpleCriteriaSearchComponent implements OnInit { simpleCriteriaForm: FormGroup; - otherCriteriaValueEnabled: boolean = false; - otherCriteriaValueType: string = 'DATE'; + otherCriteriaValueEnabled = false; + otherCriteriaValueType = 'DATE'; selectedValueOntolonogy: any; ontologies: any; @@ -94,8 +94,8 @@ export class SimpleCriteriaSearchComponent implements OnInit { this.archiveService.getOntologiesFromJson().subscribe((data: any) => { this.ontologies = data; this.ontologies.sort(function (a: any, b: any) { - var shortNameA = a.Label; - var shortNameB = b.Label; + const shortNameA = a.Label; + const shortNameB = b.Label; return shortNameA < shortNameB ? -1 : shortNameA > shortNameB ? 1 : 0; }); }); @@ -266,7 +266,7 @@ export class SimpleCriteriaSearchComponent implements OnInit { } else { this.simpleCriteriaForm.controls.otherCriteriaValue.setValue(''); this.otherCriteriaValueEnabled = true; - let selectedValueOntolonogyValue = this.simpleCriteriaForm.get('otherCriteria').value; + const selectedValueOntolonogyValue = this.simpleCriteriaForm.get('otherCriteria').value; const selectedValueOntolonogyElt = this.ontologies.find((ontoElt: any) => ontoElt.Value === selectedValueOntolonogyValue); if (selectedValueOntolonogyElt) { this.selectedValueOntolonogy = selectedValueOntolonogyElt.Label; @@ -287,14 +287,14 @@ export class SimpleCriteriaSearchComponent implements OnInit { ) { if (keyElt && valueElt) { this.archiveExchangeDataService.addSimpleSearchCriteriaSubject({ - keyElt: keyElt, - valueElt: valueElt, - labelElt: labelElt, - keyTranslated: keyTranslated, - operator: operator, + keyElt, + valueElt, + labelElt, + keyTranslated, + operator, category: SearchCriteriaTypeEnum.FIELDS, - valueTranslated: valueTranslated, - dataType: dataType, + valueTranslated, + dataType, }); } } diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/archive.module.ts b/ui/ui-frontend/projects/archive-search/src/app/archive/archive.module.ts index 6ccf43016f507dd9bbc167bd2a9656800496b8cd..0502214078dc19465f066a8787247b46233e499d 100644 --- a/ui/ui-frontend/projects/archive-search/src/app/archive/archive.module.ts +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/archive.module.ts @@ -74,6 +74,7 @@ import { CriteriaSearchComponent } from './criteria-search/criteria-search.compo import { FilingHoldingSchemeComponent } from './filing-holding-scheme/filing-holding-scheme.component'; import { FilingHoldingNodeComponent } from './filing-holding-scheme/tree-node/filing-holding-node.component'; import { SharedModule } from './shared/shared.module'; +import { DipRequestCreateComponent } from './archive-search/dip-request-create/dip-request-create.component'; @NgModule({ imports: [ @@ -124,6 +125,7 @@ import { SharedModule } from './shared/shared.module'; AppraisalRuleSearchComponent, SimpleCriteriaSearchComponent, TitleAndDescriptionCriteriaSearchComponent, + DipRequestCreateComponent, ], schemas: [CUSTOM_ELEMENTS_SCHEMA], }) 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 3334c495dd654b5cb702023456323f3bc18f44d1..73111aed9a16b544d8c69980bef22e0dcdab4483 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 @@ -35,27 +35,29 @@ * knowledge of the CeCILL-C license and that you accept its terms. */ import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http'; -import { Injectable, LOCALE_ID, Inject } from '@angular/core'; -import { ArchiveApiService } from '../core/api/archive-api.service'; -import { SearchService } from 'ui-frontend-common'; +import { Inject, Injectable, LOCALE_ID } from '@angular/core'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { Observable, of, throwError, TimeoutError } from 'rxjs'; import { catchError, map, tap } from 'rxjs/operators'; +import { SearchService, SecurityService } from 'ui-frontend-common'; +import { ArchiveApiService } from '../core/api/archive-api.service'; +import { ExportDIPCriteriaList } from './models/dip-request-detail.interface'; import { FilingHoldingSchemeNode } from './models/node.interface'; +import { SearchResponse } from './models/search-response.interface'; import { PagedResult, ResultFacet, SearchCriteriaDto } from './models/search.criteria'; import { Unit } from './models/unit.interface'; -import { SearchResponse } from './models/search-response.interface'; -import { MatSnackBar } from '@angular/material/snack-bar'; import { VitamUISnackBarComponent } from './shared/vitamui-snack-bar'; @Injectable({ - providedIn: 'root', + providedIn: 'root' }) export class ArchiveService extends SearchService<any> { constructor( private archiveApiService: ArchiveApiService, http: HttpClient, @Inject(LOCALE_ID) private locale: string, - private snackBar: MatSnackBar + private snackBar: MatSnackBar, + private securityService: SecurityService ) { super(http, archiveApiService, 'ALL'); } @@ -69,7 +71,7 @@ export class ArchiveService extends SearchService<any> { public loadFilingHoldingSchemeTree(tenantIdentifier: number, accessContractId: string): Observable<FilingHoldingSchemeNode[]> { const headers = new HttpHeaders({ 'X-Tenant-Id': '' + tenantIdentifier, - 'X-Access-Contract-Id': accessContractId, + 'X-Access-Contract-Id': accessContractId }); return this.archiveApiService.getFilingHoldingScheme(headers).pipe( @@ -98,7 +100,7 @@ export class ArchiveService extends SearchService<any> { parents: parentNode ? [parentNode] : [], vitamId: unit['#id'], checked: false, - hidden: false, + hidden: false }; outNode.children = this.buildNestedTreeLevels(arr, outNode).sort(byTitle(this.locale)); out.push(outNode); @@ -129,7 +131,7 @@ export class ArchiveService extends SearchService<any> { this.snackBar.openFromComponent(VitamUISnackBarComponent, { panelClass: 'vitamui-snack-bar', data: { type: 'exportCsvLimitReached' }, - duration: 10000, + duration: 10000 }); } } @@ -154,20 +156,20 @@ export class ArchiveService extends SearchService<any> { } private buildPagedResults(response: SearchResponse): PagedResult { - let pagedResult: PagedResult = { + const pagedResult: PagedResult = { results: response.$results, totalResults: response.$hits.total, pageNumbers: +response.$hits.size !== 0 ? Math.floor(+response.$hits.total / +response.$hits.size) + (+response.$hits.total % +response.$hits.size === 0 ? 0 : 1) - : 0, + : 0 }; - let resultFacets: ResultFacet[] = []; + const resultFacets: ResultFacet[] = []; if (response.$facetResults && response.$facetResults) { - for (let facet of response.$facetResults) { + for (const facet of response.$facetResults) { if (facet.name === 'COUNT_BY_NODE') { - let buckets = facet.buckets; - for (let bucket of buckets) { + const buckets = facet.buckets; + for (const bucket of buckets) { resultFacets.push({ node: bucket.value, count: bucket.count }); } } @@ -213,6 +215,35 @@ export class ArchiveService extends SearchService<any> { getObjectById(id: string, headers?: HttpHeaders) { return this.archiveApiService.getObjectById(id, headers); } + + hasArchiveSearchRole(role: string, tenantIdentifier: number): Observable<boolean> { + const applicationIdentifier = 'ARCHIVE_SEARCH_MANAGEMENT_APP'; + return this.securityService.hasRole(applicationIdentifier, tenantIdentifier, role); + } + + exportDIPService(exportDIPCriteriaList: ExportDIPCriteriaList, accessContract: string): Observable<string> { + let headers = new HttpHeaders().append('Content-Type', 'application/json'); + headers = headers.append('X-Access-Contract-Id', accessContract); + return this.archiveApiService.exportDipApiService(exportDIPCriteriaList, headers); + } + + startEliminationAnalysis(criteriaDto: SearchCriteriaDto, accessContract: string) { + let headers = new HttpHeaders().append('Content-Type', 'application/json'); + headers = headers.append('X-Access-Contract-Id', accessContract); + return this.archiveApiService.startEliminationAnalysis(criteriaDto, headers); + } + + openSnackBarForWorkflow(message: string, serviceUrl?: string) { + this.snackBar.openFromComponent(VitamUISnackBarComponent, { + panelClass: 'vitamui-snack-bar', + data: { + type: 'WorkflowSuccessSnackBar', + message, + serviceUrl, + }, + duration: 100000, + }); + } } function idExists(units: Unit[], id: string): boolean { diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/models/dip-request-detail.interface.ts b/ui/ui-frontend/projects/archive-search/src/app/archive/models/dip-request-detail.interface.ts new file mode 100644 index 0000000000000000000000000000000000000000..de3d0246af5bf3ddb83f0bfa8e0c7f59de04272b --- /dev/null +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/models/dip-request-detail.interface.ts @@ -0,0 +1,54 @@ +/* + * 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. + */ + +import { SearchCriteriaEltDto } from './search.criteria'; + +export interface ExportDIPRequestDetail { + messageRequestIdentifier: string; + requesterIdentifier: string; + archivalAgencyIdentifier: string; + authorizationRequestReplyIdentifier: string; + submissionAgencyIdentifier: string; + comment: string; +} + +export interface ExportDIPCriteriaList { + dipRequestParameters: ExportDIPRequestDetail; + exportDIPSearchCriteria: SearchCriteriaEltDto[]; + dataObjectVersions: string[]; + lifeCycleLogs: boolean; +} diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.component.html b/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.component.html index be37b5ad3ba83fdbd2a627ccfc2cb3f8080b26bd..fead6f8aa03ded4da0759a4a05ec9dbeb18ac058 100644 --- a/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.component.html +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.component.html @@ -1,25 +1,35 @@ <div class="vitamui-snack-bar-container"> - <span class="vitamui-snack-bar-content" [ngSwitch]="data?.type"> - <ng-container *ngSwitchCase="'sipUploaded'"> <i class="vitamui-icon vitamui-icon-archive-ingest"></i> Un ou plusieurs versement(s) sont en cours - <br/> + <br /> <span> (Merci de ne pas éteindre votre navigateur pendant le processus)</span> - </ng-container> + </ng-container> <ng-container *ngSwitchCase="'searchCriteriaHistoryCreated'"> - {{'ARCHIVE_SEARCH.SEARCH_CRITERIA_SAVER.SAVED_WITH_SUCCESS' | translate}} "<strong>{{ data?.name }}</strong>" + {{ 'ARCHIVE_SEARCH.SEARCH_CRITERIA_SAVER.SAVED_WITH_SUCCESS' | translate }} "<strong>{{ data?.name }}</strong + >" </ng-container> - <ng-container *ngSwitchCase="'exportCsvLimitReached'"> - <span> {{'ARCHIVE_SEARCH.EXPORT_CSV.EXPORT_CSV_LIMIT_REACHED' | translate}} </span> + <ng-container *ngSwitchCase="'exportCsvLimitReached'"> + <span> {{ 'ARCHIVE_SEARCH.EXPORT_CSV.EXPORT_CSV_LIMIT_REACHED' | translate }} </span> </ng-container> <ng-container *ngSwitchCase="'searchCriteriaHistoryDeleted'"> - {{'ARCHIVE_SEARCH.SEARCH_CRITERIA_SAVER.DELETED_WITH_SUCCESS_1' | translate}} "<strong>{{ data?.name }}</strong> " {{'ARCHIVE_SEARCH.SEARCH_CRITERIA_SAVER.DELETED_WITH_SUCCESS_2' | translate}} + {{ 'ARCHIVE_SEARCH.SEARCH_CRITERIA_SAVER.DELETED_WITH_SUCCESS_1' | translate }} "<strong>{{ data?.name }}</strong> " + {{ 'ARCHIVE_SEARCH.SEARCH_CRITERIA_SAVER.DELETED_WITH_SUCCESS_2' | translate }} + </ng-container> + <ng-container *ngSwitchCase="'WorkflowSuccessSnackBar'"> + <span class="space-style">{{ data?.message }}</span> + <button type="button" class="btn white" (click)="goToLogbook(data?.serviceUrl)"> + {{ 'ARCHIVE_SEARCH.DIP.TO_OPERATION_APP' | translate }} + </button> + </ng-container> + <ng-container *ngSwitchCase="'exportDIPLimitReached'"> + {{ 'ARCHIVE_SEARCH.DIP.EXPORT_LIMITS' | translate }} + </ng-container> + <ng-container *ngSwitchCase="'thresholdExceeded'"> + {{ 'ARCHIVE_SEARCH.ELIMINATION.THRESHOLD_EXCEEDED' | translate }} </ng-container> </span> - <button class="close-btn" (click)="close()"><i class="material-icons">clear</i></button> - </div> diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.component.scss b/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.component.scss index 271075ea58cd8666f5d23300e9df469e773f08e4..34207cea0d4453254c32dfc70092b86ed86b2c87 100644 --- a/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.component.scss +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.component.scss @@ -1,13 +1,16 @@ :host { - display: flex; - align-items: center; - justify-content: space-between; - height: 100%; - width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + height: 100%; + width: 100%; } .btn-link { - color: white; - font-weight: bold; - margin-left: 10px; + color: white; + font-weight: bold; + margin-left: 10px; +} +.space-style { + margin-right: 30px; } diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.component.ts b/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.component.ts index 32676bf43e3c273809a30fc5b475c0767ab99bd4..5453e100c2ae8a864cae8c3ef1b9f13a61a2665d 100644 --- a/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.component.ts +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.component.ts @@ -35,7 +35,7 @@ * knowledge of the CeCILL-C license and that you accept its terms. */ import { Component, Inject } from '@angular/core'; -import { MAT_SNACK_BAR_DATA, MatSnackBarRef } from '@angular/material/snack-bar'; +import { MatSnackBarRef, MAT_SNACK_BAR_DATA } from '@angular/material/snack-bar'; @Component({ selector: 'app-vitamui-snack-bar', @@ -43,11 +43,13 @@ import { MAT_SNACK_BAR_DATA, MatSnackBarRef } from '@angular/material/snack-bar' styleUrls: ['./vitamui-snack-bar.component.scss'] }) export class VitamUISnackBarComponent { - constructor(@Inject(MAT_SNACK_BAR_DATA) public data: any, private matSnackBarRef: MatSnackBarRef<VitamUISnackBarComponent>) {} close() { this.matSnackBarRef.dismiss(); } + goToLogbook(url: string) { + window.location.href = url; + } } diff --git a/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.service.ts b/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.service.ts index da0516140ee63c8f7fd90638ee705b47bb4055d4..8fd48aa27d3fcdb94dd34de0efd8fc62d1a0291e 100644 --- a/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.service.ts +++ b/ui/ui-frontend/projects/archive-search/src/app/archive/shared/vitamui-snack-bar/vitamui-snack-bar.service.ts @@ -37,27 +37,17 @@ // TODO Make our own snackbar service instead of ripping the code // from the angular material sources -import { LiveAnnouncer } from '@angular/cdk/a11y'; -import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; -import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; -import { ComponentPortal, ComponentType, PortalInjector, TemplatePortal } from '@angular/cdk/portal'; -import { - ComponentRef, - EmbeddedViewRef, - Inject, - Injectable, - InjectionToken, - Injector, - Optional, - SkipSelf, - TemplateRef -} from '@angular/core'; -import { MAT_SNACK_BAR_DATA, MatSnackBarConfig, MatSnackBarContainer, MatSnackBarRef, SimpleSnackBar } from '@angular/material/snack-bar'; -import { take, takeUntil } from 'rxjs/operators'; +import {LiveAnnouncer} from '@angular/cdk/a11y'; +import {BreakpointObserver,Breakpoints} from '@angular/cdk/layout'; +import {Overlay,OverlayConfig,OverlayRef} from '@angular/cdk/overlay'; +import {ComponentPortal,ComponentType,PortalInjector,TemplatePortal} from '@angular/cdk/portal'; +import {ComponentRef,EmbeddedViewRef,Inject,Injectable,InjectionToken,Injector,Optional,SkipSelf,TemplateRef} from '@angular/core'; +import {MatSnackBarConfig,MatSnackBarContainer,MatSnackBarRef,MAT_SNACK_BAR_DATA,SimpleSnackBar} from '@angular/material/snack-bar'; +import {take,takeUntil} from 'rxjs/operators'; /** Injection token that can be used to specify default snack bar. */ -export const MAT_SNACK_BAR_DEFAULT_OPTIONS = - new InjectionToken<MatSnackBarConfig>('mat-snack-bar-default-options', { +export const MAT_SNACK_BAR_DEFAULT_OPTIONS= + new InjectionToken<MatSnackBarConfig>('mat-snack-bar-default-options',{ providedIn: 'root', factory: MAT_SNACK_BAR_DEFAULT_OPTIONS_FACTORY, }); @@ -78,20 +68,20 @@ export class VitamUISnackBar { * via `_openedSnackBarRef`. */ // tslint:disable-next-line:variable-name - private _snackBarRefAtThisLevel: MatSnackBarRef<any> | null = null; + private _snackBarRefAtThisLevel: MatSnackBarRef<any>|null=null; /** Reference to the currently opened snackbar at *any* level. */ - get _openedSnackBarRef(): MatSnackBarRef<any> | null { - const parent = this._parentSnackBar; + get _openedSnackBarRef(): MatSnackBarRef<any>|null { + const parent=this._parentSnackBar; - return parent ? parent._openedSnackBarRef : this._snackBarRefAtThisLevel; + return parent? parent._openedSnackBarRef:this._snackBarRefAtThisLevel; } - set _openedSnackBarRef(value: MatSnackBarRef<any> | null) { - if (this._parentSnackBar) { - this._parentSnackBar._openedSnackBarRef = value; + set _openedSnackBarRef(value: MatSnackBarRef<any>|null) { + if(this._parentSnackBar) { + this._parentSnackBar._openedSnackBarRef=value; } else { - this._snackBarRefAtThisLevel = value; + this._snackBarRefAtThisLevel=value; } } @@ -107,7 +97,7 @@ export class VitamUISnackBar { // tslint:disable-next-line:variable-name @Optional() @SkipSelf() private _parentSnackBar: VitamUISnackBar, // tslint:disable-next-line:variable-name - @Inject(MAT_SNACK_BAR_DEFAULT_OPTIONS) private _defaultConfig: MatSnackBarConfig) { } + @Inject(MAT_SNACK_BAR_DEFAULT_OPTIONS) private _defaultConfig: MatSnackBarConfig) {} /** * Creates and dispatches a snack bar with a custom component for the content, removing any @@ -116,9 +106,9 @@ export class VitamUISnackBar { * @param component Component to be instantiated. * @param config Extra configuration for the snack bar. */ - openFromComponent<T>(component: ComponentType<T>, config?: MatSnackBarConfig): + openFromComponent<T>(component: ComponentType<T>,config?: MatSnackBarConfig): MatSnackBarRef<T> { - return this._attach(component, config) as MatSnackBarRef<T>; + return this._attach(component,config) as MatSnackBarRef<T>; } /** @@ -128,9 +118,9 @@ export class VitamUISnackBar { * @param template Template to be instantiated. * @param config Extra configuration for the snack bar. */ - openFromTemplate(template: TemplateRef<any>, config?: MatSnackBarConfig): + openFromTemplate(template: TemplateRef<any>,config?: MatSnackBarConfig): MatSnackBarRef<EmbeddedViewRef<any>> { - return this._attach(template, config); + return this._attach(template,config); } /** @@ -139,23 +129,23 @@ export class VitamUISnackBar { * @param action The label for the snackbar action. * @param config Additional configuration options for the snackbar. */ - open(message: string, action: string = '', config?: MatSnackBarConfig): + open(message: string,action: string='',config?: MatSnackBarConfig): MatSnackBarRef<SimpleSnackBar> { - const mergedConfig = { ...this._defaultConfig, ...config }; + const mergedConfig={...this._defaultConfig,...config}; // Since the user doesn't have access to the component, we can // override the data to pass in our own message and action. - mergedConfig.data = { message, action }; - mergedConfig.announcementMessage = message; + mergedConfig.data={message,action}; + mergedConfig.announcementMessage=message; - return this.openFromComponent(SimpleSnackBar, mergedConfig); + return this.openFromComponent(SimpleSnackBar,mergedConfig); } /** * Dismisses the currently-visible snack bar. */ dismiss(): void { - if (this._openedSnackBarRef) { + if(this._openedSnackBarRef) { this._openedSnackBarRef.dismiss(); } } @@ -164,17 +154,17 @@ export class VitamUISnackBar { * Attaches the snack bar container component to the overlay. */ private _attachSnackBarContainer(overlayRef: OverlayRef, - config: MatSnackBarConfig): MatSnackBarContainer { + config: MatSnackBarConfig): MatSnackBarContainer { - const userInjector = config && config.viewContainerRef && config.viewContainerRef.injector; - const injector = new PortalInjector(userInjector || this._injector, new WeakMap([ - [MatSnackBarConfig, config], + const userInjector=config&&config.viewContainerRef&&config.viewContainerRef.injector; + const injector=new PortalInjector(userInjector||this._injector,new WeakMap([ + [MatSnackBarConfig,config], ])); - const containerPortal = - new ComponentPortal(MatSnackBarContainer, config.viewContainerRef, injector); - const containerRef: ComponentRef<MatSnackBarContainer> = overlayRef.attach(containerPortal); - containerRef.instance.snackBarConfig = config; + const containerPortal= + new ComponentPortal(MatSnackBarContainer,config.viewContainerRef,injector); + const containerRef: ComponentRef<MatSnackBarContainer>=overlayRef.attach(containerPortal); + containerRef.instance.snackBarConfig=config; return containerRef.instance; } @@ -182,28 +172,28 @@ export class VitamUISnackBar { /** * Places a new component or a template as the content of the snack bar container. */ - private _attach<T>(content: ComponentType<T> | TemplateRef<T>, userConfig?: MatSnackBarConfig): - MatSnackBarRef<T | EmbeddedViewRef<any>> { + private _attach<T>(content: ComponentType<T>|TemplateRef<T>,userConfig?: MatSnackBarConfig): + MatSnackBarRef<T|EmbeddedViewRef<any>> { - const config = { ...new MatSnackBarConfig(), ...this._defaultConfig, ...userConfig }; - const overlayRef = this._createOverlay(config); - const container = this._attachSnackBarContainer(overlayRef, config); - const snackBarRef = new MatSnackBarRef<T | EmbeddedViewRef<any>>(container, overlayRef); + const config={...new MatSnackBarConfig(),...this._defaultConfig,...userConfig}; + const overlayRef=this._createOverlay(config); + const container=this._attachSnackBarContainer(overlayRef,config); + const snackBarRef=new MatSnackBarRef<T|EmbeddedViewRef<any>>(container,overlayRef); - if (content instanceof TemplateRef) { - const portal = new TemplatePortal(content, null, { + if(content instanceof TemplateRef) { + const portal=new TemplatePortal(content,null,{ $implicit: config.data, snackBarRef } as any); - snackBarRef.instance = container.attachTemplatePortal(portal); + snackBarRef.instance=container.attachTemplatePortal(portal); } else { - const injector = this._createInjector(config, snackBarRef); - const portal = new ComponentPortal(content, undefined, injector); - const contentRef = container.attachComponentPortal<T>(portal); + const injector=this._createInjector(config,snackBarRef); + const portal=new ComponentPortal(content,undefined,injector); + const contentRef=container.attachComponentPortal<T>(portal); // We can't pass this via the injector, because the injector is created earlier. - snackBarRef.instance = contentRef.instance; + snackBarRef.instance=contentRef.instance; } // Subscribe to the breakpoint observer and attach the mat-snack-bar-handset class as @@ -212,30 +202,30 @@ export class VitamUISnackBar { this._breakpointObserver.observe(Breakpoints.Handset).pipe( takeUntil(overlayRef.detachments().pipe(take(1))) ).subscribe((state) => { - if (state.matches) { + if(state.matches) { overlayRef.overlayElement.classList.add('mat-snack-bar-handset'); } else { overlayRef.overlayElement.classList.remove('mat-snack-bar-handset'); } }); - this._animateSnackBar(snackBarRef, config); - this._openedSnackBarRef = snackBarRef; + this._animateSnackBar(snackBarRef,config); + this._openedSnackBarRef=snackBarRef; return this._openedSnackBarRef; } /** Animates the old snack bar out and the new one in. */ - private _animateSnackBar(snackBarRef: MatSnackBarRef<any>, config: MatSnackBarConfig) { + private _animateSnackBar(snackBarRef: MatSnackBarRef<any>,config: MatSnackBarConfig) { // When the snackbar is dismissed, clear the reference to it. snackBarRef.afterDismissed().subscribe(() => { // Clear the snackbar ref if it hasn't already been replaced by a newer snackbar. - if (this._openedSnackBarRef === snackBarRef) { - this._openedSnackBarRef = null; + if(this._openedSnackBarRef===snackBarRef) { + this._openedSnackBarRef=null; } }); - if (this._openedSnackBarRef) { + if(this._openedSnackBarRef) { // If a snack bar is already in view, dismiss it and enter the // new snack bar after exit animation is complete. this._openedSnackBarRef.afterDismissed().subscribe(() => { @@ -248,12 +238,12 @@ export class VitamUISnackBar { } // If a dismiss timeout is provided, set up dismiss based on after the snackbar is opened. - if (config.duration && config.duration > 0) { + if(config.duration&&config.duration>0) { snackBarRef.afterOpened().subscribe(() => snackBarRef._dismissAfter(config.duration)); } - if (config.announcementMessage) { - this._live.announce(config.announcementMessage, config.politeness); + if(config.announcementMessage) { + this._live.announce(config.announcementMessage,config.politeness); } } @@ -262,26 +252,26 @@ export class VitamUISnackBar { * @param config The user-specified snack bar config. */ private _createOverlay(config: MatSnackBarConfig): OverlayRef { - const overlayConfig = new OverlayConfig(); - overlayConfig.direction = config.direction; + const overlayConfig=new OverlayConfig(); + overlayConfig.direction=config.direction; - const positionStrategy = this._overlay.position().global(); + const positionStrategy=this._overlay.position().global(); // Set horizontal position. - const isRtl = config.direction === 'rtl'; - const isLeft = ( - config.horizontalPosition === 'left' || - (config.horizontalPosition === 'start' && !isRtl) || - (config.horizontalPosition === 'end' && isRtl)); - const isRight = !isLeft && config.horizontalPosition !== 'center'; - if (isLeft) { + const isRtl=config.direction==='rtl'; + const isLeft=( + config.horizontalPosition==='left'|| + (config.horizontalPosition==='start'&&!isRtl)|| + (config.horizontalPosition==='end'&&isRtl)); + const isRight=!isLeft&&config.horizontalPosition!=='center'; + if(isLeft) { positionStrategy.left('0'); - } else if (isRight) { + } else if(isRight) { positionStrategy.right('0'); } else { positionStrategy.centerHorizontally(); } // Set horizontal position. - if (config.verticalPosition === 'top') { + if(config.verticalPosition==='top') { positionStrategy.top('0'); } else { positionStrategy.bottom('0'); @@ -291,9 +281,9 @@ export class VitamUISnackBar { // The whole file has been ripped from the angular material sources // (https://github.com/angular/material2/blob/master/src/lib/snack-bar/snack-bar.ts) // just to add the line below - overlayConfig.width = '100%'; + overlayConfig.width='100%'; - overlayConfig.positionStrategy = positionStrategy; + overlayConfig.positionStrategy=positionStrategy; return this._overlay.create(overlayConfig); } @@ -307,11 +297,12 @@ export class VitamUISnackBar { config: MatSnackBarConfig, snackBarRef: MatSnackBarRef<T>): PortalInjector { - const userInjector = config && config.viewContainerRef && config.viewContainerRef.injector; + const userInjector=config&&config.viewContainerRef&&config.viewContainerRef.injector; - return new PortalInjector(userInjector || this._injector, new WeakMap<any, any>([ - [MatSnackBarRef, snackBarRef], - [MAT_SNACK_BAR_DATA, config.data], + return new PortalInjector(userInjector||this._injector,new WeakMap<any,any>([ + [MatSnackBarRef,snackBarRef], + [MAT_SNACK_BAR_DATA,config.data], ])); } + } 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 d03e78d4a5aaf73a394bcfb13396423cbc660de5..9bc2f6e404b95a26e17dfcd726e187072d48dcf1 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 @@ -34,14 +34,15 @@ * 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. */ -import { Inject, Injectable } from '@angular/core'; import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http'; -import { BASE_URL, BaseHttpClient, PageRequest, PaginatedResponse } from 'ui-frontend-common'; +import { Inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; -import { SearchCriteriaDto } from '../../archive/models/search.criteria'; -import { SearchResponse } from '../../archive/models/search-response.interface'; +import { BaseHttpClient, BASE_URL, PageRequest, PaginatedResponse } from 'ui-frontend-common'; +import { ExportDIPCriteriaList } from '../../archive/models/dip-request-detail.interface'; import { SearchCriteriaHistory } from '../../archive/models/search-criteria-history.interface'; +import { SearchResponse } from '../../archive/models/search-response.interface'; +import { SearchCriteriaDto } from '../../archive/models/search.criteria'; @Injectable({ providedIn: 'root', @@ -49,47 +50,47 @@ import { SearchCriteriaHistory } from '../../archive/models/search-criteria-hist export class ArchiveApiService extends BaseHttpClient<any> { baseUrl: string; - constructor(http: HttpClient, @Inject(BASE_URL) baseUrl: string) { - super(http, baseUrl + '/archive-search'); - this.baseUrl = baseUrl; + constructor(http: HttpClient,@Inject(BASE_URL) baseUrl: string) { + super(http,baseUrl+'/archive-search'); + this.baseUrl=baseUrl; } getBaseUrl() { return this.baseUrl; } - getAllPaginated(pageRequest: PageRequest, embedded?: string, headers?: HttpHeaders): Observable<PaginatedResponse<any>> { + getAllPaginated(pageRequest: PageRequest,embedded?: string,headers?: HttpHeaders): Observable<PaginatedResponse<any>> { return super - .getAllPaginated(pageRequest, embedded, headers) - .pipe(tap((result) => result.values.map((ev) => (ev.parsedData = ev.data != null ? JSON.parse(ev.data) : null)))); + .getAllPaginated(pageRequest,embedded,headers) + .pipe(tap((result) => result.values.map((ev) => (ev.parsedData=ev.data!=null? JSON.parse(ev.data):null)))); } getFilingHoldingScheme(headers?: HttpHeaders): Observable<SearchResponse> { - return this.http.get<SearchResponse>(this.apiUrl + '/filingholdingscheme', { headers }); + return this.http.get<SearchResponse>(this.apiUrl+'/filingholdingscheme',{headers}); } - get(unitId: string, headers?: HttpHeaders): Observable<SearchResponse> { - return this.http.get<any>(this.apiUrl + '/units/' + unitId, { headers }); + get(unitId: string,headers?: HttpHeaders): Observable<SearchResponse> { + return this.http.get<any>(this.apiUrl+'/units/'+unitId,{headers}); } - searchArchiveUnitsByCriteria(criteriaDto: SearchCriteriaDto, headers?: HttpHeaders): Observable<SearchResponse> { - return this.http.post<SearchResponse>(`${this.apiUrl}/search`, criteriaDto, { headers }); + searchArchiveUnitsByCriteria(criteriaDto: SearchCriteriaDto,headers?: HttpHeaders): Observable<SearchResponse> { + return this.http.post<SearchResponse>(`${this.apiUrl}/search`,criteriaDto,{headers}); } - exportCsvSearchArchiveUnitsByCriteria(criteriaDto: SearchCriteriaDto, headers?: HttpHeaders): Observable<Blob> { - return this.http.post(`${this.apiUrl}/export-csv-search`, criteriaDto, { + exportCsvSearchArchiveUnitsByCriteria(criteriaDto: SearchCriteriaDto,headers?: HttpHeaders): Observable<Blob> { + return this.http.post(`${this.apiUrl}/export-csv-search`,criteriaDto,{ responseType: 'blob', - headers: headers, + headers, }); } - downloadObjectFromUnit(id: string, headers?: HttpHeaders): Observable<HttpResponse<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' }); + return this.http.get(`${this.apiUrl}/downloadobjectfromunit/${id}`,{headers,observe: 'response',responseType: 'blob'}); } - findArchiveUnit(id: string, headers?: HttpHeaders): Observable<any> { - return this.http.get(`${this.apiUrl}/archiveunit/${id}`, { headers: headers, responseType: 'text' }); + findArchiveUnit(id: string,headers?: HttpHeaders): Observable<any> { + return this.http.get(`${this.apiUrl}/archiveunit/${id}`,{headers,responseType: 'text'}); } getSearchCriteriaHistory(): Observable<SearchCriteriaHistory[]> { @@ -97,7 +98,7 @@ export class ArchiveApiService extends BaseHttpClient<any> { } saveSearchCriteriaHistory(searchCriteriaHistory: SearchCriteriaHistory): Observable<SearchCriteriaHistory> { - return this.http.post<SearchCriteriaHistory>(`${this.apiUrl}/searchcriteriahistory`, searchCriteriaHistory); + return this.http.post<SearchCriteriaHistory>(`${this.apiUrl}/searchcriteriahistory`,searchCriteriaHistory); } deleteSearchCriteriaHistory(id: string): Observable<void> { @@ -105,9 +106,22 @@ export class ArchiveApiService extends BaseHttpClient<any> { } updateSearchCriteriaHistory(searchCriteriaHistory: SearchCriteriaHistory): Observable<SearchCriteriaHistory> { - return this.http.put<SearchCriteriaHistory>(`${this.apiUrl}/searchcriteriahistory/${searchCriteriaHistory.id}`, 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' }); + getObjectById(id: string,headers?: HttpHeaders): Observable<any> { + return this.http.get(`${this.apiUrl}/object/${id}`,{headers,responseType: 'text'}); + } + + exportDipApiService(exportDIPCriteriaList: ExportDIPCriteriaList, headers?: HttpHeaders): Observable<string> { + return this.http.post(`${this.apiUrl}/export-dip`, exportDIPCriteriaList, { + responseType: 'text', + headers, + }); + } + + startEliminationAnalysis(criteriaDto: SearchCriteriaDto, headers?: HttpHeaders): Observable<any> { + return this.http.post(`${this.apiUrl}/elimination/analysis`, criteriaDto, { + headers + }); } } diff --git a/ui/ui-frontend/projects/archive-search/src/assets/i18n/en.json b/ui/ui-frontend/projects/archive-search/src/assets/i18n/en.json index ffd82ab40b6f237e46d9ee831db0b656e2c22a9d..6b8f5ba4cdfca5ef04b4cffff19a500d957cda63 100644 --- a/ui/ui-frontend/projects/archive-search/src/assets/i18n/en.json +++ b/ui/ui-frontend/projects/archive-search/src/assets/i18n/en.json @@ -1,5 +1,34 @@ { "ARCHIVE_SEARCH": { + "DIP" : { + "EXPORT_DIP" : "Export a DIP", + "SELECTED" :"Selected", + "ONE_SELECTED" :"Selected", + "CANCEL" : "Cancel", + "REQUEST_ID" :"Request identifier", + "REQUESTER_ID":"Requester identifier", + "COMMENT" : "Comment(s)", + "ARCHIVAL_AGENCY" :"Archive service identifier", + "AUTHORISATION_REQUEST_REPLY" : "Identifier of the response to an authorization request", + "SUBMISSION_AGENCY_ID" : "Submission agency identifier ", + "REQUIRED": "Required field", + "REQUEST_DIP" : "Request to export a DIP", + "DIP_PARAMS" : "Define the DIP parameters", + "ITEMS_SELECTED" : "Items selected", + "ONE_ITEM_SELECTED" : "Item selected", + "LOGS" : "Life cycle logs :", + "INCLUDE" : "Include", + "EXCLUDE" :"Exclude", + "OBJECT_GROUPS" : "groups of objects :", + "REQUEST_MESSAGE" : "Your DIP request has been taken into account ", + "TO_OPERATION_APP" : "SEE OPERATIONS LOG", + "EXPORT_LIMITS": "DIP export cannot be performed. Please restrict your selection to a maximum of 10,000 archival units" + }, + "ELIMINATION": { + "ANALYSIS": "Launch elimination analysis", + "ELIMINATION_LAUNCHED": "Your request for elimination has been taken into account", + "THRESHOLD_EXCEEDED": "The elimination analysis cannot be started. Please restrict your selection to a maximum of 10,000 archival units." + }, "ACCESS_CONTRACT_NOT_FOUND": "No contract associated with current user", "SHOW_TREES_PLANS": "Show trees and plans", "TITLE_SEARCH": "Search : ", @@ -16,6 +45,28 @@ "TITLE": "Search filters", "DUA_TITLE": "Administrative useful life", "OTHER_FILED_SELECTION": "Select another criteria", + "APPRAISAL_RULE": { + "APPRAISAL_RULE_END_DATE_END": "DUA: max End date", + "APPRAISAL_RULE_END_DATE_BEGIN": "DUA: min End date", + "CREATE_INTERVAL": "Create an interval", + "DELETE_INTERVAL": "delete interval", + "APPRAISAL_RULE_FINAL_ACTION": "FInal Action", + "APPRAISAL_RULE_TITLE": "DUA Rule title", + "APPRAISAL_RULE_IDENTIFIER": "DUA Rule identifier", + "APPRAISAL_RULE_START_DATE": "DUA Start Date", + "APPRAISAL_RULE_END_DATE": "DUAEnd Date", + "APPRAISAL_RULE_FINAL_ACTION_TYPE": "Final Action", + "APPRAISAL_RULE_ORIGIN": "DUA Rule", + "APPRAISAL_RULE_FINAL_ACTION_TYPE_ELIMINATION": "Elimination", + "APPRAISAL_RULE_FINAL_ACTION_TYPE_KEEP": "Conservation", + "APPRAISAL_RULE_FINAL_ACTION_TYPE_NOT_SPECIFIED": "Not declared", + "APPRAISAL_RULE_FINAL_ACTION_HAS_FINAL_ACTION": "Holden rule", + "APPRAISAL_RULE_FINAL_ACTION_INHERITE_FINAL_ACTION": "Inherited", + "APPRAISAL_RULE_ORIGIN_HAS_AT_LEAST_ONE": "Holden rule", + "APPRAISAL_RULE_ORIGIN_HAS_NO_ONE": "Not defined", + "APPRAISAL_RULE_ORIGIN_WAITING_RECALCULATE": "Waiting for recalculate", + "APPRAISAL_RULE_ORIGIN_INHERITE_AT_LEAST_ONE": "Inherited" + }, "FIELDS": { "ID": "ID", "TITLE_OR_DESCRIPTION": "Title or Description", @@ -47,7 +98,8 @@ "HAS_NO_APPRAISAL_RULE": "Does not have a DUA rule defined", "WAITING_TO_RE_CALCULATE_APPRAISAL_RULE": "Is waiting for rule recalculation", "SELECT_ALL_APPRAISAL_RULE": "Select all" - } + }, + "ELIMINATION_TECHNICAL_ID": "Elimination analysis operation Identifier" } }, "ARCHIVE_UNIT": { @@ -93,7 +145,6 @@ "SHOW_TREE_PARENT": "Collapse the tree" }, "EXPORT_CSV": { - "EXPORT_SEARCHS": "Export results", "EXPORT_CSV_LIMIT_REACHED": "It's not possible to export more than 10 000 results, please update your filters." }, "RESULTS": "Results", diff --git a/ui/ui-frontend/projects/archive-search/src/assets/i18n/fr.json b/ui/ui-frontend/projects/archive-search/src/assets/i18n/fr.json index 32207fc503140007080bf1bdef94ae0148e20064..685e1ec1330706ef154d6da177496be8b5279b79 100644 --- a/ui/ui-frontend/projects/archive-search/src/assets/i18n/fr.json +++ b/ui/ui-frontend/projects/archive-search/src/assets/i18n/fr.json @@ -1,5 +1,34 @@ { "ARCHIVE_SEARCH": { + "DIP" : { + "EXPORT_DIP" : "Exporter un DIP", + "SELECTED" :"Sélectionnés", + "ONE_SELECTED" :"Sélectionné", + "CANCEL" : "Annuler", + "REQUEST_ID" :"Identifiant de la demande", + "REQUESTER_ID":"Identifiant du demandeur", + "COMMENT" : "Commentaire(s)", + "ARCHIVAL_AGENCY" :"Identifiant du service d’archives", + "AUTHORISATION_REQUEST_REPLY" : "Identifiant de la réponse à une demande d’autorisation ", + "SUBMISSION_AGENCY_ID" : "Identifiant du service versant ", + "REQUIRED": "Champ requis", + "REQUEST_DIP" : "Demande d'export d'un DIP", + "DIP_PARAMS" : "Définir les paramètres du DIP", + "ITEMS_SELECTED" : "éléments sélectionnés", + "ONE_ITEM_SELECTED" : "élément sélectionné", + "LOGS" : "Journaux :", + "INCLUDE" : "Inclure", + "EXCLUDE" :"Exclure", + "OBJECT_GROUPS" : "Groupes d'objets :", + "REQUEST_MESSAGE" : "Votre demande de DIP a bien été prise en compte ", + "TO_OPERATION_APP" : "VOIR LE JOURNAL DES OPÉRATIONS", + "EXPORT_LIMITS" : "L’export de DIP ne peut être réalisé. Merci de restreindre votre sélection à 10 000 unités archivistiques maximum." + }, + "ELIMINATION": { + "ANALYSIS": "Lancer une analyse d'élimination", + "ELIMINATION_LAUNCHED": "Votre demande d'élimination a bien prise en compte", + "THRESHOLD_EXCEEDED": "L’analyse d’élimination ne peut être lancée. Merci de restreindre votre sélection à 10 000 unités archivistiques maximum." + }, "ACCESS_CONTRACT_NOT_FOUND": "Aucun contrat d'accès n'est associé à l'utiisateur", "SHOW_TREES_PLANS": "Afficher les arbres et plans", "TITLE_SEARCH": "Recherche : ", @@ -144,7 +173,6 @@ "SHOW_TREE_PARENT": "Réduire l'arborescence" }, "EXPORT_CSV": { - "EXPORT_SEARCHS": "Exporter les résultats", "EXPORT_CSV_LIMIT_REACHED": "Il n'est pas possible de réaliser un export csv sur plus de 10 000 résultats de recherche, veuillez restreindre votre sélection ou affiner votre recherche." }, "RESULTS": "résultats", diff --git a/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-download.service.ts b/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-download.service.ts index 23a5a21e0bfbcc644b4f08c744f1e58c0ed0b188..a15a48d79d6ee8520efef3b6e9cb3f4be1c676dd 100644 --- a/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-download.service.ts +++ b/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-download.service.ts @@ -34,36 +34,30 @@ * 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. */ -import {HttpClient,HttpHeaders} from '@angular/common/http'; -import {Injectable} from '@angular/core'; -import {Subject} from 'rxjs'; -import {Event,LogbookApiService,SearchService,VitamUISnackBar} from 'ui-frontend-common'; -import {VitamUISnackBarComponent} from '../shared/vitamui-snack-bar'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; +import { Event, LogbookApiService, SearchService, VitamUISnackBar } from 'ui-frontend-common'; - -const DOWNLOAD_TYPE_TRANSFER_SIP='transfersip'; -const DOWNLOAD_TYPE_DIP='dip'; -const DOWNLOAD_TYPE_BATCH_REPORT='batchreport'; -const DOWNLOAD_TYPE_REPORT='report'; -const DOWNLOAD_TYPE_OBJECT='object'; +const DOWNLOAD_TYPE_TRANSFER_SIP = 'transfersip'; +const DOWNLOAD_TYPE_DIP = 'dip'; +const DOWNLOAD_TYPE_BATCH_REPORT = 'batchreport'; +const DOWNLOAD_TYPE_REPORT = 'report'; +const DOWNLOAD_TYPE_OBJECT = 'object'; @Injectable({ providedIn: 'root' }) export class LogbookDownloadService extends SearchService<Event> { + updated = new Subject<Event>(); - updated=new Subject<Event>(); - - constructor( - private logbookApiService: LogbookApiService, - private snackBar: VitamUISnackBar, - http: HttpClient) { - super(http,logbookApiService); + constructor(private logbookApiService: LogbookApiService, private snackBar: VitamUISnackBar, http: HttpClient) { + super(http, logbookApiService); } isOperationInProgress(event: Event): boolean { - const status=this.getOperationStatus(event); - switch(status) { + const status = this.getOperationStatus(event); + switch (status) { case 'STARTED': case 'En cours': return true; @@ -73,11 +67,11 @@ export class LogbookDownloadService extends SearchService<Event> { } getOperationStatus(event: Event): string { - const eventsLength=event.events.length; + const eventsLength = event.events.length; - if(eventsLength>0) { - if(event.type===event.events[eventsLength-1].type) { - return event.events[eventsLength-1].outcome; + if (eventsLength > 0) { + if (event.type === event.events[eventsLength - 1].type) { + return event.events[eventsLength - 1].outcome; } else { return 'En cours'; } @@ -87,16 +81,28 @@ export class LogbookDownloadService extends SearchService<Event> { } canDownloadReports(event: Event): string[] { - const evType=event.type.toUpperCase(); - const evTypeProc=event.typeProc.toUpperCase(); + const evType = event.type.toUpperCase(); + const evTypeProc = event.typeProc.toUpperCase(); // tslint:disable-next-line: max-line-length - const evTypeAllowed=['STP_IMPORT_RULES','IMPORT_AGENCIES','HOLDINGSCHEME','IMPORT_ONTOLOGY','STP_REFERENTIAL_FORMAT_IMPORT','DATA_MIGRATION','ELIMINATION_ACTION','IMPORT_PRESERVATION_SCENARIO','IMPORT_GRIFFIN','STP_IMPORT_GRIFFIN','PRESERVATION','INGEST_CLEANUP']; - const evTypeProcAllowed=['AUDIT','EXPORT_DIP','ARCHIVE_TRANSFER','TRANSFER_REPLY','INGEST','MASS_UPDATE']; - - - if(evTypeProcAllowed.includes(evTypeProc)||evTypeAllowed.includes(evType)) { - if(this.isOperationInProgress(event)) { + const evTypeAllowed = [ + 'STP_IMPORT_RULES', + 'IMPORT_AGENCIES', + 'HOLDINGSCHEME', + 'IMPORT_ONTOLOGY', + 'STP_REFERENTIAL_FORMAT_IMPORT', + 'DATA_MIGRATION', + 'ELIMINATION_ACTION', + 'IMPORT_PRESERVATION_SCENARIO', + 'IMPORT_GRIFFIN', + 'STP_IMPORT_GRIFFIN', + 'PRESERVATION', + 'INGEST_CLEANUP' + ]; + const evTypeProcAllowed = ['AUDIT', 'EXPORT_DIP', 'ARCHIVE_TRANSFER', 'TRANSFER_REPLY', 'INGEST', 'MASS_UPDATE']; + + if (evTypeProcAllowed.includes(evTypeProc) || evTypeAllowed.includes(evType)) { + if (this.isOperationInProgress(event)) { return ['in-progress']; } else { return ['download']; @@ -106,13 +112,13 @@ export class LogbookDownloadService extends SearchService<Event> { } } - getDownloadType(eventTypeProc: string,eventType: string): string { - switch(eventTypeProc) { + getDownloadType(eventTypeProc: string, eventType: string): string { + switch (eventTypeProc) { case 'AUDIT': - if(eventType==='EXPORT_PROBATIVE_VALUE'||eventType==='RECTIFICATION_AUDIT') { + if (eventType === 'EXPORT_PROBATIVE_VALUE' || eventType === 'RECTIFICATION_AUDIT') { return DOWNLOAD_TYPE_REPORT; } - if(eventType==='EVIDENCE_AUDIT'||eventType==='PROCESS_AUDIT') { + if (eventType === 'EVIDENCE_AUDIT' || eventType === 'PROCESS_AUDIT') { return DOWNLOAD_TYPE_BATCH_REPORT; } case 'DATA_MIGRATION': @@ -129,7 +135,7 @@ export class LogbookDownloadService extends SearchService<Event> { case 'ARCHIVE_TRANSFER': return DOWNLOAD_TYPE_TRANSFER_SIP; case 'MASTERDATA': - switch(eventType) { + switch (eventType) { case 'STP_IMPORT_RULES': case 'IMPORT_AGENCIES': case 'IMPORT_ONTOLOGY': @@ -141,7 +147,7 @@ export class LogbookDownloadService extends SearchService<Event> { return DOWNLOAD_TYPE_OBJECT; } case 'INTERNAL_OPERATING_OP': - if(eventType==='INGEST_CLEANUP') { + if (eventType === 'INGEST_CLEANUP') { return DOWNLOAD_TYPE_BATCH_REPORT; } break; @@ -150,54 +156,51 @@ export class LogbookDownloadService extends SearchService<Event> { } } - downloadReport(event: Event,tenantIdentifier: number,accessContractId: string) { - if(this.isOperationInProgress(event)) { + downloadReport(event: Event, tenantIdentifier: number, accessContractId: string) { + if (this.isOperationInProgress(event)) { return; } - const id=event.id; + const id = event.id; - this.snackBar.openFromComponent(VitamUISnackBarComponent,{ - panelClass: 'vitamui-snack-bar', - duration: 10000, - data: {type: 'eventExportAll'} - }); + var eventTypeProc = event.typeProc.toUpperCase(); + var eventType = event.type.toUpperCase(); + var downloadType = this.getDownloadType(eventTypeProc, eventType); - var eventTypeProc=event.typeProc.toUpperCase(); - var eventType=event.type.toUpperCase(); - var downloadType=this.getDownloadType(eventTypeProc,eventType); - - if(downloadType) { - const headers=new HttpHeaders({ + if (downloadType) { + const headers = new HttpHeaders({ 'X-Tenant-Id': tenantIdentifier.toString(), 'X-Access-Contract-Id': accessContractId }); - this.logbookApiService.downloadReport(id,downloadType,headers).subscribe((response) => { - const element=document.createElement('a'); - const blob=new Blob([response.body],{type: 'octet/stream'}); - const url=window.URL.createObjectURL(blob); - element.href=url; - element.download=id+'.json'; - if(DOWNLOAD_TYPE_OBJECT==downloadType) { - element.download=id+'.xml'; - } - if(DOWNLOAD_TYPE_BATCH_REPORT==downloadType) { - element.download=id+'.jsonl'; + this.logbookApiService.downloadReport(id, downloadType, headers).subscribe( + (response) => { + const element = document.createElement('a'); + const blob = new Blob([response.body], { type: 'octet/stream' }); + const url = window.URL.createObjectURL(blob); + element.href = url; + element.download = id + '.json'; + if (DOWNLOAD_TYPE_OBJECT == downloadType) { + element.download = id + '.xml'; + } + if (DOWNLOAD_TYPE_BATCH_REPORT == downloadType) { + element.download = id + '.jsonl'; + } + if (DOWNLOAD_TYPE_DIP == downloadType) { + element.download = id + '.zip'; + } + element.click(); + window.URL.revokeObjectURL(url); + }, + (error) => { + this.snackBar.open(error.error.message, null, { + panelClass: 'vitamui-snack-bar', + duration: 10000 + }); } - if(DOWNLOAD_TYPE_DIP==downloadType) { - element.download=id+'.zip'; - } - element.click(); - window.URL.revokeObjectURL(url); - },(error) => { - this.snackBar.open(error.error.message,null,{ - panelClass: 'vitamui-snack-bar', - duration: 10000 - }); - }); + ); } else { - this.snackBar.open('Impossible de télécharger le rapport pour cette opération',null,{ + this.snackBar.open('Impossible de télécharger le rapport pour cette opération', null, { panelClass: 'vitamui-snack-bar', duration: 10000 }); diff --git a/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.component.spec.ts b/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.component.spec.ts index 814f416526f240c727224fe3b4994dcd6043906d..00c73c7a7a1f6796096b80d9251bc19d07da02f3 100644 --- a/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.component.spec.ts +++ b/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.component.spec.ts @@ -35,16 +35,14 @@ * knowledge of the CeCILL-C license and that you accept its terms. */ -import { ENVIRONMENT, GlobalEventService, InjectorModule, LoggerModule } from 'ui-frontend-common'; - import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { MatMenuModule } from '@angular/material/menu'; import { ActivatedRoute, Router } from '@angular/router'; - import { EMPTY, of } from 'rxjs'; +import { ENVIRONMENT, GlobalEventService, InjectorModule, LoggerModule, SearchBarComponent, SearchBarModule } from 'ui-frontend-common'; import { environment } from '../../environments/environment'; import { LogbookOperationComponent } from './logbook-operation.component'; import { LogbookSearchService } from './logbook-search.service'; @@ -53,29 +51,25 @@ describe('LogbookOperationComponent', () => { let component: LogbookOperationComponent; let fixture: ComponentFixture<LogbookOperationComponent>; - beforeEach(waitForAsync(() => { - const matDialogSpy = jasmine.createSpyObj('MatDialog', ['open']); - matDialogSpy.open.and.returnValue({ afterClosed: () => of(true) }); - TestBed.configureTestingModule({ - imports: [ - MatMenuModule, - ReactiveFormsModule, - InjectorModule, - LoggerModule.forRoot() - ], - declarations: [LogbookOperationComponent], - providers: [ - { provide: MatDialog, useValue: matDialogSpy }, - { provide: ActivatedRoute, useValue: { paramMap: EMPTY, data: EMPTY } }, - { provide: LogbookSearchService, useValue: { search: () => EMPTY } }, - { provide: Router, useValue: { navigate: () => { } } }, - GlobalEventService, - { provide: ENVIRONMENT, useValue: environment } - ], - schemas: [NO_ERRORS_SCHEMA] + beforeEach( + waitForAsync(() => { + const matDialogSpy = jasmine.createSpyObj('MatDialog', ['open']); + matDialogSpy.open.and.returnValue({ afterClosed: () => of(true) }); + TestBed.configureTestingModule({ + imports: [MatMenuModule, ReactiveFormsModule, InjectorModule, LoggerModule.forRoot(), SearchBarModule], + declarations: [LogbookOperationComponent, SearchBarComponent], + providers: [ + { provide: MatDialog, useValue: matDialogSpy }, + { provide: ActivatedRoute, useValue: { paramMap: EMPTY, data: EMPTY, queryParams: of({ guid: 'operationId' }) } }, + { provide: LogbookSearchService, useValue: { search: () => EMPTY } }, + { provide: Router, useValue: { navigate: () => {} } }, + GlobalEventService, + { provide: ENVIRONMENT, useValue: environment } + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); }) - .compileComponents(); - })); + ); beforeEach(() => { fixture = TestBed.createComponent(LogbookOperationComponent); diff --git a/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.component.ts b/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.component.ts index 51cfbbb44f8dbb8c6f2e85b625af70dc4092847b..78d7994d66e3f1b5188c81ba9aced497f3fa90d1 100644 --- a/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.component.ts +++ b/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.component.ts @@ -34,13 +34,11 @@ * 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. */ -import { GlobalEventService, SearchBarComponent, SidenavPage } from 'ui-frontend-common'; - import { Component, OnInit, ViewChild } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Router } from '@angular/router'; - -import {MatDialog} from '@angular/material/dialog'; +import { GlobalEventService, SearchBarComponent, SidenavPage } from 'ui-frontend-common'; import { EventFilter } from './event-filter.interface'; import { LogbookOperationListComponent } from './logbook-operation-list/logbook-operation-list.component'; @@ -50,14 +48,13 @@ import { LogbookOperationListComponent } from './logbook-operation-list/logbook- styleUrls: ['./logbook-operation.component.scss'] }) export class LogbookOperationComponent extends SidenavPage<any> implements OnInit { - search = ''; dateRangeFilterForm: FormGroup; tenantIdentifier: number; filters: Readonly<EventFilter> = {}; + workflowGuidToSearch: string; - - @ViewChild(SearchBarComponent, {static: true}) searchBar: SearchBarComponent; + @ViewChild(SearchBarComponent, { static: true }) searchBar: SearchBarComponent; @ViewChild(LogbookOperationListComponent, { static: true }) list: LogbookOperationListComponent; constructor( @@ -69,7 +66,7 @@ export class LogbookOperationComponent extends SidenavPage<any> implements OnIni ) { super(route, globalEventService); - this.route.paramMap.subscribe((paramMap) => this.tenantIdentifier = + paramMap.get('tenantIdentifier')); + this.route.paramMap.subscribe((paramMap) => (this.tenantIdentifier = +paramMap.get('tenantIdentifier'))); this.dateRangeFilterForm = this.formBuilder.group({ startDate: null, @@ -86,11 +83,25 @@ export class LogbookOperationComponent extends SidenavPage<any> implements OnIni } ngOnInit() { + this.route.queryParams.subscribe((params) => { + if (params.guid) { + this.workflowGuidToSearch = params.guid; + this.searchBar.searchValue = this.workflowGuidToSearch; + this.onSearchSubmit(this.workflowGuidToSearch); + this.openOperationDetail(); + } + }); if (!this.list) { console.error('LogbookOperationComponent Error: no list in the template'); } } + openOperationDetail() { + setTimeout(() => { + this.list.eventClick.emit(this.list.dataSource[0]); + }, 2000); + } + changeTenant(tenantIdentifier: number) { this.router.navigate(['..', tenantIdentifier], { relativeTo: this.route }); } diff --git a/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.module.ts b/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.module.ts index c4bf621fc28132314926d2e2d44ba8f18986ac0c..432bd1bd6c9147f274177dc65cfecc303eceddaf 100644 --- a/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.module.ts +++ b/ui/ui-frontend/projects/referential/src/app/logbook-operation/logbook-operation.module.ts @@ -34,37 +34,33 @@ * 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. */ -import {TableFilterModule, VitamUICommonModule} from 'ui-frontend-common'; - -import {CommonModule} from '@angular/common'; -import {NgModule} from '@angular/core'; -import {ReactiveFormsModule} from '@angular/forms'; -import {MatDialogModule} from '@angular/material/dialog'; -import {MatNativeDateModule} from '@angular/material/core'; -import {MatDatepickerModule} from '@angular/material/datepicker'; -import {MatMenuModule} from '@angular/material/menu'; -import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; -import {MatSidenavModule} from '@angular/material/sidenav'; -import {MatTabsModule} from '@angular/material/tabs'; -import {MatSnackBarModule} from '@angular/material/snack-bar'; -import {MatSelectModule} from '@angular/material/select'; - -import { - LogbookOperationDetailComponent -} from './logbook-operation-detail/logbook-operation-detail.component'; -import { - LogbookOperationPopupComponent -} from './logbook-operation-detail/logbook-operation-popup.component'; -import {EventTypeBadgeClassPipe} from './logbook-operation-list/event-type-badge-class.pipe'; -import {EventTypeColorClassPipe} from './logbook-operation-list/event-type-color-class.pipe'; -import {LastEventPipe} from './logbook-operation-list/last-event.pipe'; -import {LogbookOperationListComponent} from './logbook-operation-list/logbook-operation-list.component'; -import {LogbookOperationRoutingModule} from './logbook-operation-routing.module'; -import {LogbookOperationComponent} from './logbook-operation.component'; +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { MatNativeDateModule } from '@angular/material/core'; +import { MatDatepickerModule } from '@angular/material/datepicker'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatSelectModule } from '@angular/material/select'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { TableFilterModule, VitamUICommonModule } from 'ui-frontend-common'; +import { LogbookOperationDetailComponent } from './logbook-operation-detail/logbook-operation-detail.component'; +import { LogbookOperationPopupComponent } from './logbook-operation-detail/logbook-operation-popup.component'; +import { EventTypeBadgeClassPipe } from './logbook-operation-list/event-type-badge-class.pipe'; +import { EventTypeColorClassPipe } from './logbook-operation-list/event-type-color-class.pipe'; +import { LastEventPipe } from './logbook-operation-list/last-event.pipe'; +import { LogbookOperationListComponent } from './logbook-operation-list/logbook-operation-list.component'; +import { LogbookOperationRoutingModule } from './logbook-operation-routing.module'; +import { LogbookOperationComponent } from './logbook-operation.component'; @NgModule({ declarations: [ LogbookOperationComponent, + LogbookOperationListComponent, LogbookOperationDetailComponent, LogbookOperationPopupComponent, @@ -86,7 +82,8 @@ import {LogbookOperationComponent} from './logbook-operation.component'; LogbookOperationRoutingModule, MatNativeDateModule, MatSelectModule, - TableFilterModule - ] + TableFilterModule, + MatTooltipModule, + ], }) -export class LogbookOperationModule { } +export class LogbookOperationModule {}