MapstructElasticMapper.java
/*
* Copyright 2025 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys.server.api.v2.mapper;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.genesys.blocks.security.SecurityContextUtil;
import org.genesys.blocks.security.model.AclAwareModel;
import org.genesys.blocks.util.JsonSidConverter.SidProvider;
import org.genesys.server.model.dataset.QDatasetAccessionRef;
import org.genesys.server.model.genesys.Accession;
import org.genesys.server.model.genesys.AccessionId;
import org.genesys.server.model.genesys.AccessionList;
import org.genesys.server.model.impl.QDiversityTreeAccessionRef;
import org.genesys.server.model.impl.QSubsetAccessionRef;
import org.mapstruct.InjectionStrategy;
import org.mapstruct.Mapper;
import org.mapstruct.MapperConfig;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import lombok.extern.slf4j.Slf4j;
import javax.validation.constraints.NotNull;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.FIELD, config = MapstructElasticMapper.DataMappingsConfig.class)
@Slf4j
public abstract class MapstructElasticMapper {
@Autowired
private SidProvider sidNameProvider;
@Autowired
private JPAQueryFactory jpaQueryFactory;
// Configs and utils
public <A, B> B[] map(@NotNull A[] source, @NotNull Function<A, B> mapper, @NotNull B[] array) {
var res = new ArrayList<B>(source.length);
for (A a : source) {
res.add(mapper.apply(a));
}
return res.toArray(array);
}
public <A, B> List<B> map(@NotNull Collection<A> source, @NotNull Function<A, B> mapper) {
if (source == null) return null;
var res = new LinkedList<B>();
source.forEach(x -> res.add(mapper.apply(x)));
return res;
}
public <A, B> Page<B> map(@NotNull Page<A> source, @NotNull Function<A, B> mapper) {
if (source == null) return null;
return source.map(mapper);
}
@Named("getAclSid")
public String getAclSid(Long id) {
if (id == null) {
// Don't write SID name
return null;
}
return sidNameProvider == null ? id.toString() : sidNameProvider.getSidName(id);
}
@Named("isPublic")
static boolean isPublic(AclAwareModel model) {
if (model.getId() == null) {
// Don't write permissions for non-persisted objects
return false;
}
try {
// StopWatch stopWatch = StopWatch.createStarted();
var result = SecurityContextUtil.anyoneHasPermission(model, "READ");
// log.warn("isPublic in {}ms", stopWatch.getTime());
return result;
} catch (Throwable e) {
return false;
}
}
@MapperConfig(imports = {
Objects.class
})
interface DataMappingsConfig {
@Retention(RetentionPolicy.CLASS)
@Mapping(source = "createdBy", target = "createdBy", qualifiedByName = "getAclSid")
@Mapping(source = "lastModifiedBy", target = "lastModifiedBy", qualifiedByName = "getAclSid")
public @interface AuditNames {
}
@Retention(RetentionPolicy.CLASS)
@Mapping(target = "createdBy", ignore = true)
@Mapping(target = "lastModifiedBy", ignore = true)
public @interface IgnoreAudit {
}
@Retention(RetentionPolicy.CLASS)
@Mapping(target = "public", source = ".", qualifiedByName = "isPublic")
public @interface IsPublic {
}
@Retention(RetentionPolicy.CLASS)
@Mapping(source = ".", target = "_permissions", qualifiedByName = "getPermissions")
public @interface IncludePermissions {
}
@Retention(RetentionPolicy.CLASS)
@Mapping(target = "_permissions", ignore = true)
public @interface IgnorePermissions {
}
}
@Mapping(source = ".", target = "datasets", qualifiedByName = "mapDatasetUUIDs")
@Mapping(source = ".", target = "subsets", qualifiedByName = "mapSubsetUUIDs")
@Mapping(source = ".", target = "diversityTrees", qualifiedByName = "mapDiversityTreeUUIDs")
public abstract org.genesys.server.model.elastic.AccessionDTO mapES(Accession accession);
@Mapping(source = "lists", target = "lists", qualifiedByName = "mapAccessionListUUIDs")
public abstract org.genesys.server.model.elastic.AccessionIdDTO mapES(AccessionId accession);
@Named("mapDatasetUUIDs")
protected List<UUID> mapDatasetUUIDs(Accession accession) {
return jpaQueryFactory
.from(QDatasetAccessionRef.datasetAccessionRef)
.select(QDatasetAccessionRef.datasetAccessionRef.list().uuid)
.where(QDatasetAccessionRef.datasetAccessionRef.accession().eq(accession))
.fetch();
}
@Named("mapSubsetUUIDs")
protected List<UUID> mapSubsetUUIDs(Accession accession) {
return jpaQueryFactory
.from(QSubsetAccessionRef.subsetAccessionRef)
.select(QSubsetAccessionRef.subsetAccessionRef.list().uuid)
.where(QSubsetAccessionRef.subsetAccessionRef.accession().eq(accession))
.fetch();
}
@Named("mapDiversityTreeUUIDs")
protected List<UUID> mapDiversityTreeUUIDs(Accession accession) {
return jpaQueryFactory
.from(QDiversityTreeAccessionRef.diversityTreeAccessionRef)
.select(QDiversityTreeAccessionRef.diversityTreeAccessionRef.list().uuid)
.where(QDiversityTreeAccessionRef.diversityTreeAccessionRef.accession().eq(accession))
.fetch();
}
@Named("mapAccessionListUUIDs")
protected Set<UUID> mapAccessionListUUIDs(Set<AccessionList> uuidModels) {
return uuidModels.stream().map(AccessionList::getUuid).collect(Collectors.toSet());
}
}