From e6e881d6b018b13f8bb9563f032d6812095b8fb2 Mon Sep 17 00:00:00 2001 From: jadz94 Date: Tue, 26 Nov 2024 11:07:45 +0100 Subject: [PATCH] chore: (ART-10688) refactor ckan mapper --- pom.xml | 13 + .../BeaconIndividualsRequestMapper.java | 11 +- .../ckan/mapper/CkanMapper.java | 100 ++++ .../persistence/CkanDatasetsRepository.java | 11 +- .../ckan/utils/CkanAgentParser.java | 44 -- .../ckan/utils/CkanDatetimeParser.java | 41 -- .../ckan/utils/CkanTagParser.java | 32 -- .../ckan/utils/CkanValueLabelParser.java | 37 -- .../ckan/utils/PackageSearchMapper.java | 45 -- .../ckan/utils/PackageShowMapper.java | 142 ----- .../services/PackageShowMapperTest.java | 483 ++++++++++-------- 11 files changed, 400 insertions(+), 559 deletions(-) create mode 100644 src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/mapper/CkanMapper.java delete mode 100644 src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanAgentParser.java delete mode 100644 src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanDatetimeParser.java delete mode 100644 src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanTagParser.java delete mode 100644 src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanValueLabelParser.java delete mode 100644 src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/PackageSearchMapper.java delete mode 100644 src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/PackageShowMapper.java diff --git a/pom.xml b/pom.xml index e998356..6dd1003 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,7 @@ 3.5.2 1.4.0 0.8.12 + 1.5.5.Final @@ -138,6 +139,17 @@ quarkus-jacoco test + + org.mapstruct + mapstruct + ${mapstruct.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + provided + @@ -172,6 +184,7 @@ -parameters + -Amapstruct.unmappedTargetPolicy=ERROR diff --git a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/beacon/persistence/BeaconIndividualsRequestMapper.java b/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/beacon/persistence/BeaconIndividualsRequestMapper.java index 079a12c..fb06914 100644 --- a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/beacon/persistence/BeaconIndividualsRequestMapper.java +++ b/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/beacon/persistence/BeaconIndividualsRequestMapper.java @@ -9,10 +9,7 @@ import static java.util.stream.Collectors.toMap; import io.github.genomicdatainfrastructure.discovery.datasets.domain.exceptions.InvalidFacetException; -import io.github.genomicdatainfrastructure.discovery.model.DatasetSearchQuery; -import io.github.genomicdatainfrastructure.discovery.model.DatasetSearchQueryFacet; -import io.github.genomicdatainfrastructure.discovery.model.FilterType; -import io.github.genomicdatainfrastructure.discovery.model.QueryEntry; +import io.github.genomicdatainfrastructure.discovery.model.*; import io.github.genomicdatainfrastructure.discovery.remote.beacon.model.BeaconIndividualsRequest; import io.github.genomicdatainfrastructure.discovery.remote.beacon.model.BeaconIndividualsRequestMeta; import io.github.genomicdatainfrastructure.discovery.remote.beacon.model.BeaconIndividualsRequestQuery; @@ -29,7 +26,7 @@ public class BeaconIndividualsRequestMapper { private static final String BEACON_FACET_GROUP = "beacon"; private static final String SCOPE = "individual"; - private static final String INCLUDE_RESULTSET_RESPONSES = "HIT"; + private static final String INCLUDE_RESULT_SET_RESPONSES = "HIT"; private static final String REQUESTED_GRANULARITY = "record"; public BeaconIndividualsRequest from( @@ -56,7 +53,7 @@ public BeaconIndividualsRequest from( return BeaconIndividualsRequest.builder() .meta(new BeaconIndividualsRequestMeta()) .query(BeaconIndividualsRequestQuery.builder() - .includeResultsetResponses(INCLUDE_RESULTSET_RESPONSES) + .includeResultsetResponses(INCLUDE_RESULT_SET_RESPONSES) .requestedGranularity(REQUESTED_GRANULARITY) .testMode(false) .pagination(new BeaconIndividualsRequestQueryPagination()) @@ -90,7 +87,7 @@ private BeaconIndividualsRequestQueryFilter buildDropdownBeaconFacet( private BeaconIndividualsRequestQueryFilter buildFreeTextBeaconFacet( DatasetSearchQueryFacet facet) { String operator = ofNullable(facet.getOperator()) - .map(it -> it.value()) + .map(Operator::value) .orElseThrow(() -> new InvalidFacetException( "Facet operator must not be null")); diff --git a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/mapper/CkanMapper.java b/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/mapper/CkanMapper.java new file mode 100644 index 0000000..26fb158 --- /dev/null +++ b/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/mapper/CkanMapper.java @@ -0,0 +1,100 @@ +package io.github.genomicdatainfrastructure.discovery.datasets.infrastructure.ckan.mapper; + +import io.github.genomicdatainfrastructure.discovery.model.RetrievedDataset; +import io.github.genomicdatainfrastructure.discovery.model.RetrievedDistribution; +import io.github.genomicdatainfrastructure.discovery.model.SearchedDataset; +import io.github.genomicdatainfrastructure.discovery.model.ValueLabel; +import io.github.genomicdatainfrastructure.discovery.remote.ckan.model.*; +import org.apache.commons.lang3.StringUtils; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.NullValueMappingStrategy; + +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static org.mapstruct.CollectionMappingStrategy.ADDER_PREFERRED; +import static org.mapstruct.NullValueCheckStrategy.ALWAYS; + +@Mapper(componentModel = "jakarta", nullValueCheckStrategy = ALWAYS, nullValueIterableMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT, collectionMappingStrategy = ADDER_PREFERRED) +public interface CkanMapper { + + DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern( + "yyyy-MM-dd'T'HH:mm:ss.SSSSSS"); + + @Mapping(target = "description", source = "notes") + @Mapping(target = "themes", source = "theme") + @Mapping(target = "contacts", source = "contact") + @Mapping(target = "distributions", source = "resources") + @Mapping(target = "keywords", source = "tags") + @Mapping(target = "spatial", source = "spatialUri") + @Mapping(target = "createdAt", source = "issued") + @Mapping(target = "modifiedAt", source = "modified") + @Mapping(target = "creators", source = "creator") + @Mapping(target = "hasVersions", source = "hasVersion") + @Mapping(target = "publishers", source = "publisher") + @Mapping(target = "languages", source = "language") + @Mapping(target = "catalogue", ignore = true) + RetrievedDataset map(CkanPackage ckanPackage); + + @Mapping(target = "label", source = "displayName") + @Mapping(target = "value", source = "name") + @Mapping(target = "count", ignore = true) + ValueLabel map(CkanValueLabel ckanValueLabel); + + @Mapping(target = "label", source = "displayName") + @Mapping(target = "value", source = "name") + @Mapping(target = "count", ignore = true) + ValueLabel map(CkanTag ckanTag); + + @Mapping(target = "title", source = "name") + @Mapping(target = "createdAt", source = "issuedDate") + @Mapping(target = "modifiedAt", source = "modifiedDate") + @Mapping(target = "languages", source = "language") + RetrievedDistribution map(CkanResource ckanResource); + + default List map(PackagesSearchResult result) { + if (result == null || result.getResults() == null) { + return Collections.emptyList(); + } + return result.getResults().stream() + .map(this::mapToSearchedDataset) + .collect(Collectors.toList()); + } + + @Mapping(target = "description", source = "notes") + @Mapping(target = "themes", source = "theme") + @Mapping(target = "publishers", source = "publisher") + @Mapping(target = "keywords", source = "tags") + @Mapping(target = "modifiedAt", source = "modified") + @Mapping(target = "createdAt", source = "issued") + @Mapping(target = "distributionsCount", expression = "java(ckanPackage.getResources()!= null ? ckanPackage.getResources().size():0)") + @Mapping(target = "catalogue", ignore = true) + @Mapping(target = "recordsCount", ignore = true) + SearchedDataset mapToSearchedDataset(CkanPackage ckanPackage); + + default OffsetDateTime map(String date) { + if (StringUtils.isBlank(date)) { + return null; + } + + try { + return OffsetDateTime.parse(date); + } catch (DateTimeParseException e) { + var dateToParse = date; + if (dateToParse.matches("^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$")) { + dateToParse += "T00:00:00.000000"; + } + return LocalDateTime.parse(dateToParse, DATE_FORMATTER) + .truncatedTo(ChronoUnit.SECONDS) + .atOffset(ZoneOffset.UTC); + } + } +} diff --git a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/persistence/CkanDatasetsRepository.java b/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/persistence/CkanDatasetsRepository.java index cbcac54..117897f 100644 --- a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/persistence/CkanDatasetsRepository.java +++ b/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/persistence/CkanDatasetsRepository.java @@ -6,11 +6,10 @@ import io.github.genomicdatainfrastructure.discovery.datasets.application.ports.DatasetsRepository; import io.github.genomicdatainfrastructure.discovery.datasets.domain.exceptions.DatasetNotFoundException; +import io.github.genomicdatainfrastructure.discovery.datasets.infrastructure.ckan.mapper.CkanMapper; import io.github.genomicdatainfrastructure.discovery.model.*; import io.github.genomicdatainfrastructure.discovery.remote.ckan.api.CkanQueryApi; import io.github.genomicdatainfrastructure.discovery.remote.ckan.model.*; -import io.github.genomicdatainfrastructure.discovery.datasets.infrastructure.ckan.utils.PackageSearchMapper; -import io.github.genomicdatainfrastructure.discovery.datasets.infrastructure.ckan.utils.PackageShowMapper; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.ws.rs.WebApplicationException; @@ -26,12 +25,14 @@ public class CkanDatasetsRepository implements DatasetsRepository { private final CkanQueryApi ckanQueryApi; + private final CkanMapper ckanMapper; @Inject public CkanDatasetsRepository( - @RestClient CkanQueryApi ckanQueryApi + @RestClient CkanQueryApi ckanQueryApi, CkanMapper ckanMapper ) { this.ckanQueryApi = ckanQueryApi; + this.ckanMapper = ckanMapper; } @Override @@ -72,14 +73,14 @@ public List search( request ); - return PackageSearchMapper.from(response.getResult()); + return ckanMapper.map(response.getResult()); } @Override public RetrievedDataset findById(String id, String accessToken) { try { var ckanPackage = ckanQueryApi.packageShow(id); - return PackageShowMapper.from(ckanPackage.getResult()); + return ckanMapper.map(ckanPackage.getResult()); } catch (WebApplicationException e) { if (e.getResponse().getStatus() == 404) { throw new DatasetNotFoundException(id); diff --git a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanAgentParser.java b/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanAgentParser.java deleted file mode 100644 index 7bc508b..0000000 --- a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanAgentParser.java +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: 2024 PNED G.I.E. -// -// SPDX-License-Identifier: Apache-2.0 - -package io.github.genomicdatainfrastructure.discovery.datasets.infrastructure.ckan.utils; - -import io.github.genomicdatainfrastructure.discovery.model.Agent; -import io.github.genomicdatainfrastructure.discovery.remote.ckan.model.CkanAgent; -import lombok.experimental.UtilityClass; - -import java.util.List; -import java.util.Objects; - -import static java.util.Optional.ofNullable; - -@UtilityClass -public class CkanAgentParser { - - public List agents(List agents) { - return ofNullable(agents) - .orElseGet(List::of) - .stream() - .map(CkanAgentParser::agent) - .filter(Objects::nonNull) - .toList(); - } - - public Agent agent(CkanAgent agent) { - if (agent == null) { - return null; - } - - return Agent.builder() - .name(agent.getName()) - .email(agent.getEmail()) - .url(agent.getUrl()) - .uri(agent.getUri()) - .type(agent.getType()) - .identifier(agent.getIdentifier()) - .build(); - - } - -} diff --git a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanDatetimeParser.java b/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanDatetimeParser.java deleted file mode 100644 index b79c126..0000000 --- a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanDatetimeParser.java +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: 2024 PNED G.I.E. -// -// SPDX-License-Identifier: Apache-2.0 - -package io.github.genomicdatainfrastructure.discovery.datasets.infrastructure.ckan.utils; - -import lombok.experimental.UtilityClass; -import org.apache.commons.lang3.StringUtils; - -import java.time.LocalDateTime; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.time.temporal.ChronoUnit; - -// TODO review original field and date format on resources -@UtilityClass -public class CkanDatetimeParser { - - private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern( - "yyyy-MM-dd'T'HH:mm:ss.SSSSSS"); - - public OffsetDateTime datetime(String date) { - if (StringUtils.isBlank(date)) { - return null; - } - - try { - return OffsetDateTime.parse(date); - } catch (DateTimeParseException e) { - var dateToParse = date; - if (dateToParse.matches("^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$")) { - dateToParse += "T00:00:00.000000"; - } - return LocalDateTime.parse(dateToParse, DATE_FORMATTER) - .truncatedTo(ChronoUnit.SECONDS) - .atOffset(ZoneOffset.UTC); - } - } -} diff --git a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanTagParser.java b/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanTagParser.java deleted file mode 100644 index 216a85d..0000000 --- a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanTagParser.java +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: 2024 PNED G.I.E. -// -// SPDX-License-Identifier: Apache-2.0 - -package io.github.genomicdatainfrastructure.discovery.datasets.infrastructure.ckan.utils; - -import io.github.genomicdatainfrastructure.discovery.model.ValueLabel; -import io.github.genomicdatainfrastructure.discovery.remote.ckan.model.CkanTag; -import lombok.experimental.UtilityClass; - -import java.util.List; - -import static java.util.Optional.ofNullable; - -@UtilityClass -public class CkanTagParser { - - public List keywords(List tags) { - return ofNullable(tags) - .orElseGet(List::of) - .stream() - .map(CkanTagParser::keyword) - .toList(); - } - - private ValueLabel keyword(CkanTag ckanTag) { - return ValueLabel.builder() - .label(ckanTag.getDisplayName()) - .value(ckanTag.getName()) - .build(); - } -} diff --git a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanValueLabelParser.java b/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanValueLabelParser.java deleted file mode 100644 index b699909..0000000 --- a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/CkanValueLabelParser.java +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-FileCopyrightText: 2024 PNED G.I.E. -// -// SPDX-License-Identifier: Apache-2.0 - -package io.github.genomicdatainfrastructure.discovery.datasets.infrastructure.ckan.utils; - -import io.github.genomicdatainfrastructure.discovery.model.ValueLabel; -import io.github.genomicdatainfrastructure.discovery.remote.ckan.model.CkanValueLabel; -import lombok.experimental.UtilityClass; - -import java.util.List; -import java.util.Objects; - -import static java.util.Optional.ofNullable; - -@UtilityClass -public class CkanValueLabelParser { - - public List values(List values) { - return ofNullable(values) - .orElseGet(List::of) - .stream() - .map(CkanValueLabelParser::value) - .filter(Objects::nonNull) - .toList(); - } - - public ValueLabel value(CkanValueLabel value) { - return ofNullable(value) - .map(it -> ValueLabel.builder() - .value(value.getName()) - .label(value.getDisplayName()) - .build()) - .orElse(null); - - } -} diff --git a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/PackageSearchMapper.java b/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/PackageSearchMapper.java deleted file mode 100644 index 769cf66..0000000 --- a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/PackageSearchMapper.java +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: 2024 PNED G.I.E. -// -// SPDX-License-Identifier: Apache-2.0 - -package io.github.genomicdatainfrastructure.discovery.datasets.infrastructure.ckan.utils; - -import io.github.genomicdatainfrastructure.discovery.model.SearchedDataset; -import io.github.genomicdatainfrastructure.discovery.remote.ckan.model.*; -import lombok.experimental.UtilityClass; -import org.apache.commons.lang3.ObjectUtils; - -import java.util.List; - -import static java.util.Optional.ofNullable; - -@UtilityClass -public class PackageSearchMapper { - - public List from(PackagesSearchResult result) { - var nonNullPackages = ofNullable(result) - .map(PackagesSearchResult::getResults) - .filter(ObjectUtils::isNotEmpty) - .orElseGet(List::of); - - return nonNullPackages.stream() - .map(PackageSearchMapper::result) - .toList(); - } - - private SearchedDataset result(CkanPackage ckanPackage) { - - return SearchedDataset.builder() - .id(ckanPackage.getId()) - .identifier(ckanPackage.getIdentifier()) - .title(ckanPackage.getTitle()) - .description(ckanPackage.getNotes()) - .themes(CkanValueLabelParser.values(ckanPackage.getTheme())) - .keywords(CkanTagParser.keywords(ckanPackage.getTags())) - .publishers(CkanAgentParser.agents(ckanPackage.getPublisher())) - .modifiedAt(CkanDatetimeParser.datetime(ckanPackage.getModified())) - .createdAt(CkanDatetimeParser.datetime(ckanPackage.getIssued())) - .distributionsCount(ckanPackage.getResources().size()) - .build(); - } -} diff --git a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/PackageShowMapper.java b/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/PackageShowMapper.java deleted file mode 100644 index 80d1b1f..0000000 --- a/src/main/java/io/github/genomicdatainfrastructure/discovery/datasets/infrastructure/ckan/utils/PackageShowMapper.java +++ /dev/null @@ -1,142 +0,0 @@ -// SPDX-FileCopyrightText: 2024 PNED G.I.E. -// -// SPDX-License-Identifier: Apache-2.0 - -package io.github.genomicdatainfrastructure.discovery.datasets.infrastructure.ckan.utils; - -import io.github.genomicdatainfrastructure.discovery.model.*; -import io.github.genomicdatainfrastructure.discovery.remote.ckan.model.*; -import lombok.experimental.UtilityClass; - -import java.util.List; -import java.util.Objects; - -import static java.util.Optional.ofNullable; - -// TODO review original field and date format on resources -@UtilityClass -public class PackageShowMapper { - - public RetrievedDataset from(CkanPackage ckanPackage) { - - return RetrievedDataset.builder() - .id(ckanPackage.getId()) - .identifier(ckanPackage.getIdentifier()) - .title(ckanPackage.getTitle()) - .description(ckanPackage.getNotes()) - .themes(CkanValueLabelParser.values(ckanPackage.getTheme())) - .publishers(CkanAgentParser.agents(ckanPackage.getPublisher())) - .createdAt(CkanDatetimeParser.datetime(ckanPackage.getIssued())) - .modifiedAt(CkanDatetimeParser.datetime(ckanPackage.getModified())) - .url(ckanPackage.getUrl()) - .languages(CkanValueLabelParser.values(ckanPackage.getLanguage())) - .creators(agents(ckanPackage.getCreator())) - .publishers(agents(ckanPackage.getPublisher())) - .hasVersions(CkanValueLabelParser.values(ckanPackage.getHasVersion())) - .accessRights(CkanValueLabelParser.value(ckanPackage.getAccessRights())) - .conformsTo(CkanValueLabelParser.values(ckanPackage.getConformsTo())) - .provenance(ckanPackage.getProvenance()) - .spatial(CkanValueLabelParser.value(ckanPackage.getSpatialUri())) - .distributions(distributions(ckanPackage.getResources())) - .keywords(CkanTagParser.keywords(ckanPackage.getTags())) - .contacts(contactPoint(ckanPackage.getContact())) - .datasetRelationships(relations(ckanPackage.getDatasetRelationships())) - .dataDictionary(dictionary(ckanPackage.getDataDictionary())) - .build(); - } - - private List contactPoint(List values) { - return ofNullable(values) - .orElseGet(List::of) - .stream() - .filter(Objects::nonNull) - .map(PackageShowMapper::contactPointEntry) - .toList(); - } - - private List relations(List values) { - return ofNullable(values) - .orElseGet(List::of) - .stream() - .filter(Objects::nonNull) - .map(PackageShowMapper::relation) - .toList(); - } - - private List dictionary(List values) { - return ofNullable(values) - .orElseGet(List::of) - .stream() - .filter(Objects::nonNull) - .map(PackageShowMapper::dictionaryEntry) - .toList(); - } - - private ContactPoint contactPointEntry(CkanContactPoint value) { - return ContactPoint.builder() - .name(value.getName()) - .email(value.getEmail()) - .uri(value.getUri()) - .identifier(value.getIdentifier()) - .build(); - } - - private DatasetRelationEntry relation(CkanDatasetRelationEntry value) { - return DatasetRelationEntry.builder() - .relation(value.getRelation()) - .target(value.getTarget()) - .build(); - } - - private DatasetDictionaryEntry dictionaryEntry(CkanDatasetDictionaryEntry value) { - return DatasetDictionaryEntry.builder() - .name(value.getName()) - .type(value.getType()) - .description(value.getDescription()) - .build(); - } - - private List distributions(List resources) { - return ofNullable(resources) - .orElseGet(List::of) - .stream() - .map(PackageShowMapper::distribution) - .toList(); - } - - private RetrievedDistribution distribution(CkanResource ckanResource) { - return RetrievedDistribution.builder() - .id(ckanResource.getId()) - .title(ckanResource.getName()) - .description(ckanResource.getDescription()) - .format(CkanValueLabelParser.value(ckanResource.getFormat())) - .createdAt(CkanDatetimeParser.datetime(ckanResource.getIssuedDate())) - .modifiedAt(CkanDatetimeParser.datetime(ckanResource.getModifiedDate())) - .accessUrl(ckanResource.getAccessUrl()) - .downloadUrl(ckanResource.getDownloadUrl()) - .languages(CkanValueLabelParser.values(ckanResource.getLanguage())) - .build(); - } - - private List agents(List creators) { - return ofNullable(creators) - .orElseGet(List::of) - .stream() - .map(PackageShowMapper::agent) - .filter(Objects::nonNull) - .toList(); - } - - private Agent agent(CkanAgent value) { - return ofNullable(value) - .map(it -> Agent.builder() - .name(value.getName()) - .email(value.getEmail()) - .type(value.getType()) - .identifier(value.getIdentifier()) - .url(value.getUrl()) - .uri(value.getUri()) - .build()) - .orElse(null); - } -} diff --git a/src/test/java/io/github/genomicdatainfrastructure/discovery/services/PackageShowMapperTest.java b/src/test/java/io/github/genomicdatainfrastructure/discovery/services/PackageShowMapperTest.java index e9de549..3901060 100644 --- a/src/test/java/io/github/genomicdatainfrastructure/discovery/services/PackageShowMapperTest.java +++ b/src/test/java/io/github/genomicdatainfrastructure/discovery/services/PackageShowMapperTest.java @@ -4,104 +4,214 @@ package io.github.genomicdatainfrastructure.discovery.services; -import io.github.genomicdatainfrastructure.discovery.datasets.infrastructure.ckan.utils.PackageShowMapper; +import io.github.genomicdatainfrastructure.discovery.datasets.infrastructure.ckan.mapper.CkanMapper; +import io.github.genomicdatainfrastructure.discovery.datasets.infrastructure.ckan.mapper.CkanMapperImpl; import io.github.genomicdatainfrastructure.discovery.model.*; import io.github.genomicdatainfrastructure.discovery.remote.ckan.model.*; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import java.time.OffsetDateTime; +import java.util.Collections; import java.util.List; +import java.util.stream.Stream; import static java.time.OffsetDateTime.parse; import static org.assertj.core.api.Assertions.assertThat; class PackageShowMapperTest { - @Test - void accepts_empty_package() { - var ckanPackage = CkanPackage.builder().build(); + private final CkanMapper mapper = new CkanMapperImpl(); - var actual = PackageShowMapper.from(ckanPackage); - var expected = RetrievedDataset.builder() - .conformsTo(List.of()) - .distributions(List.of()) - .hasVersions(List.of()) - .languages(List.of()) - .themes(List.of()) - .keywords(List.of()) - .contacts(List.of()) - .creators(List.of()) - .publishers(List.of()) - .datasetRelationships(List.of()) - .dataDictionary(List.of()) - .build(); + @Nested + class RetrievedDatasetTest { + + @Test + void given_ckanPackage_with_empty_collections_should_be_mapped_to_empty_Lists() { + final var ckanPackage = CkanPackage.builder().build(); + + final var actual = mapper.map(ckanPackage); + final var expected = RetrievedDataset.builder() + .conformsTo(List.of()) + .distributions(List.of()) + .hasVersions(List.of()) + .languages(List.of()) + .themes(List.of()) + .keywords(List.of()) + .contacts(List.of()) + .creators(List.of()) + .publishers(List.of()) + .datasetRelationships(List.of()) + .dataDictionary(List.of()) + .build(); + + assertThat(actual) + .usingRecursiveComparison() + .isEqualTo(expected); + } + + @Test + void given_ckanPackage_should_be_mapped_to_expected_values() { + final var ckanPackage = buildCkanPackage(); - assertThat(actual) - .usingRecursiveComparison() - .isEqualTo(expected); + final var actual = mapper.map(ckanPackage); + final var expected = RetrievedDataset.builder() + .id("id") + .identifier("identifier") + .title("title") + .description("notes") + .themes(List.of( + ValueLabel.builder() + .value("theme-name") + .label("theme") + .build())) + .createdAt(parse("2024-07-01T22:00:00+00:00")) + .modifiedAt(parse("2024-07-02T22:00:00+00:00")) + .url("url") + .languages(List.of( + ValueLabel.builder() + .value("en") + .label("language") + .build())) + .hasVersions(List.of( + ValueLabel.builder() + .value("1") + .label("version") + .build())) + .creators(List.of( + Agent.builder() + .name("creatorName") + .identifier("creatorIdentifier") + .email("email") + .url("url") + .type("type") + .uri("uri") + .build(), + Agent.builder() + .name("creatorName2") + .identifier("creatorIdentifier2") + .email("email2") + .url("url2") + .type("type2") + .uri("uri2") + .build() + )) + .publishers(List.of( + Agent.builder() + .name("publisherName") + .identifier("publisherIdentifier") + .email("email") + .url("url") + .type("type") + .uri("uri") + .build(), + Agent.builder() + .name("publisherName2") + .identifier("publisherIdentifier2") + .email("email2") + .url("url2") + .type("type2") + .uri("uri2") + .build() + )) + .accessRights(ValueLabel.builder() + .value("public") + .label("accessRights") + .build()) + .conformsTo(List.of( + ValueLabel.builder() + .value("conforms") + .label("conformsTo") + .build())) + .provenance("provenance") + .keywords(List.of(ValueLabel.builder() + .label("key-tag") + .value("key") + .build())) + .spatial(ValueLabel.builder() + .value("uri") + .label("spatial") + .build()) + .distributions(List.of( + RetrievedDistribution.builder() + .id("resource_id") + .title("resource_name") + .description("resource_description") + .createdAt(parse("2025-03-19T00:00Z")) + .modifiedAt(parse("2025-03-19T13:37:05Z")) + .format(ValueLabel.builder() + .value("pdf") + .label("format") + .build()) + .accessUrl("accessUrl") + .downloadUrl("downloadUrl") + .languages(List.of( + ValueLabel.builder() + .value("en") + .label("language") + .build())) + .build())) + .contacts(List.of( + ContactPoint.builder() + .name("Contact 1") + .email("contact1@example.com") + .identifier("contact-identifier-1") + .build(), + ContactPoint.builder() + .name("Contact 2") + .email("contact2@example.com") + .uri("http://example.com") + .identifier("contact-identifier-2") + .build() + )) + .datasetRelationships(List.of( + DatasetRelationEntry.builder().relation("Relation 1") + .target("Dataset 1") + .build(), + DatasetRelationEntry.builder().relation("Relation 2") + .target("Dataset 2") + .build() + )) + .dataDictionary(List.of( + DatasetDictionaryEntry.builder().name("Entry 1").type("Type 1") + .description( + "Description 1") + .build(), + DatasetDictionaryEntry.builder().name("Entry 2").type("Type 2") + .description( + "Description 2") + .build() + )) + .build(); + + assertThat(actual) + .usingRecursiveComparison() + .isEqualTo(expected); + } } - @Test - void can_parse() { - var ckanPackage = CkanPackage.builder() + private static CkanPackage buildCkanPackage() { + return CkanPackage.builder() .id("id") .identifier("identifier") .title("title") .notes("notes") - .theme(List.of(CkanValueLabel.builder() - .displayName("theme") - .name("theme-name") - .build())) + .theme(getValueLabels("theme", "theme-name")) .issued("2024-07-01T22:00:00+00:00") .modified("2024-07-02T22:00:00Z") - .tags(List.of(CkanTag.builder() - .displayName("key-tag") - .id("tag-id") - .name("key") - .build())) + .tags(getCkanTags()) .url("url") - .language(List.of( - CkanValueLabel.builder() - .displayName("language") - .name("en") - .build())) - .hasVersion(List.of( - CkanValueLabel.builder() - .displayName("version") - .name("1") - .build())) - .accessRights(CkanValueLabel.builder() - .displayName("accessRights") - .name("public") - .build()) - .conformsTo(List.of( - CkanValueLabel.builder() - .displayName("conformsTo") - .name("conforms") - .build())) + .language(getValueLabels("language", "en")) + .hasVersion(getValueLabels("version", "1")) + .accessRights(getCkanValueLabel("accessRights", "public")) + .conformsTo(getValueLabels("conformsTo", "conforms")) .provenance("provenance") - .spatialUri(CkanValueLabel.builder() - .displayName("spatial") - .name("uri") - .build()) - .resources(List.of( - CkanResource.builder() - .id("resource_id") - .name("resource_name") - .description("resource_description") - .format(CkanValueLabel.builder() - .displayName("format") - .name("pdf") - .build()) - .accessUrl("accessUrl") - .downloadUrl("downloadUrl") - .issuedDate("2025-03-19") - .modifiedDate("2025-03-19T13:37:05Z") - .language(List.of( - CkanValueLabel.builder() - .displayName("language") - .name("en") - .build())) - .build())) + .spatialUri(getCkanValueLabel("spatial", "uri")) + .resources(getCkanResources()) .contact(List.of( CkanContactPoint.builder() .name("Contact 1") @@ -164,139 +274,100 @@ void can_parse() { .description("Description 2").build() )) .build(); + } - var actual = PackageShowMapper.from(ckanPackage); - var expected = RetrievedDataset.builder() - .id("id") - .identifier("identifier") - .title("title") - .description("notes") - .themes(List.of( - ValueLabel.builder() - .value("theme-name") - .label("theme") - .build())) - .createdAt(parse("2024-07-01T22:00:00+00:00")) - .modifiedAt(parse("2024-07-02T22:00:00+00:00")) - .url("url") - .languages(List.of( - ValueLabel.builder() - .value("en") - .label("language") - .build())) - .hasVersions(List.of( - ValueLabel.builder() - .value("1") - .label("version") - .build())) - .creators(List.of( - Agent.builder() - .name("creatorName") - .identifier("creatorIdentifier") - .email("email") - .url("url") - .type("type") - .uri("uri") - .build(), - Agent.builder() - .name("creatorName2") - .identifier("creatorIdentifier2") - .email("email2") - .url("url2") - .type("type2") - .uri("uri2") - .build() - )) - .publishers(List.of( - Agent.builder() - .name("publisherName") - .identifier("publisherIdentifier") - .email("email") - .url("url") - .type("type") - .uri("uri") - .build(), - Agent.builder() - .name("publisherName2") - .identifier("publisherIdentifier2") - .email("email2") - .url("url2") - .type("type2") - .uri("uri2") - .build() - )) - .accessRights(ValueLabel.builder() - .value("public") - .label("accessRights") - .build()) - .conformsTo(List.of( - ValueLabel.builder() - .value("conforms") - .label("conformsTo") - .build())) - .provenance("provenance") - .keywords(List.of(ValueLabel.builder() - .label("key-tag") - .value("key") - .build())) - .spatial(ValueLabel.builder() - .value("uri") - .label("spatial") - .build()) - .distributions(List.of( - RetrievedDistribution.builder() - .id("resource_id") - .title("resource_name") - .description("resource_description") - .createdAt(parse("2025-03-19T00:00Z")) - .modifiedAt(parse("2025-03-19T13:37:05Z")) - .format(ValueLabel.builder() - .value("pdf") - .label("format") - .build()) - .accessUrl("accessUrl") - .downloadUrl("downloadUrl") - .languages(List.of( - ValueLabel.builder() - .value("en") - .label("language") - .build())) - .build())) - .contacts(List.of( - ContactPoint.builder() - .name("Contact 1") - .email("contact1@example.com") - .identifier("contact-identifier-1") - .build(), - ContactPoint.builder() - .name("Contact 2") - .email("contact2@example.com") - .uri("http://example.com") - .identifier("contact-identifier-2") - .build() - )) - .datasetRelationships(List.of( - DatasetRelationEntry.builder().relation("Relation 1") - .target("Dataset 1") - .build(), - DatasetRelationEntry.builder().relation("Relation 2") - .target("Dataset 2") - .build() - )) - .dataDictionary(List.of( - DatasetDictionaryEntry.builder().name("Entry 1").type("Type 1") - .description( - "Description 1") - .build(), - DatasetDictionaryEntry.builder().name("Entry 2").type("Type 2") - .description( - "Description 2") - .build() - )) + @Nested + class SearchedDatasetTest { + + static Stream arguments() { + return Stream.of( + Arguments.of(null, Collections.emptyList()), // null strings should be considered blank + Arguments.of(PackagesSearchResult.builder().results(Collections.emptyList()) + .build(), Collections.emptyList()), + Arguments.of(PackagesSearchResult.builder().results(List.of(buildCkanPackage())) + .build(), List.of(buildSearchedDataset())) + ); + } + + @ParameterizedTest + @MethodSource("arguments") + void given_list_of_SearchedDataset_it_should_be_mapped_properly(PackagesSearchResult result, + List expected) { + final var actual = mapper.map(result); + assertThat(actual) + .usingRecursiveComparison() + .isEqualTo(expected); + } + + @NotNull + private static SearchedDataset buildSearchedDataset() { + return SearchedDataset.builder() + .id("id") + .identifier("identifier") + .title("title") + .description("notes") + .publishers(List.of(Agent.builder() + .name("publisherName") + .email("email") + .url("url") + .identifier("publisherIdentifier") + .uri("uri") + .type("type") + .build(), + Agent.builder() + .name("publisherName2") + .email("email2") + .url("url2") + .identifier("publisherIdentifier2") + .uri("uri2") + .type("type2") + .build())) + .themes(List.of(ValueLabel.builder() + .value("theme-name") + .label("theme") + .build())) + .keywords(List.of(ValueLabel.builder() + .value("key") + .label("key-tag") + .build())) + .modifiedAt(OffsetDateTime.parse("2024-07-02T22:00Z")) + .createdAt(OffsetDateTime.parse("2024-07-01T22:00Z")) + .distributionsCount(1) + .build(); + } + } + + private static @NotNull List getCkanResources() { + return List.of( + CkanResource.builder() + .id("resource_id") + .name("resource_name") + .description("resource_description") + .format(getCkanValueLabel("format", "pdf")) + .accessUrl("accessUrl") + .downloadUrl("downloadUrl") + .issuedDate("2025-03-19") + .modifiedDate("2025-03-19T13:37:05Z") + .language(getValueLabels("language", "en")) + .build()); + } + + private static CkanValueLabel getCkanValueLabel(String spatial, String uri) { + return CkanValueLabel.builder() + .displayName(spatial) + .name(uri) .build(); + } + + private static @NotNull List getCkanTags() { + return List.of(CkanTag.builder() + .displayName("key-tag") + .id("tag-id") + .name("key") + .build()); + } - assertThat(actual) - .usingRecursiveComparison() - .isEqualTo(expected); + private static @NotNull List getValueLabels(String theme, String name) { + return List.of(getCkanValueLabel(theme, name)); } }