argos/dmp-backend/core/src/main/java/eu/eudat/service/reference/ReferenceServiceImpl.java

507 lines
26 KiB
Java

package eu.eudat.service.reference;
import com.fasterxml.jackson.core.JsonProcessingException;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.Permission;
import eu.eudat.commons.XmlHandlingService;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.enums.ReferenceFieldDataType;
import eu.eudat.commons.enums.ReferenceSourceType;
import eu.eudat.commons.types.reference.DefinitionEntity;
import eu.eudat.commons.types.reference.FieldEntity;
import eu.eudat.commons.types.referencetype.*;
import eu.eudat.convention.ConventionService;
import eu.eudat.data.ReferenceEntity;
import eu.eudat.data.ReferenceTypeEntity;
import eu.eudat.model.Reference;
import eu.eudat.model.ReferenceType;
import eu.eudat.model.builder.ReferenceBuilder;
import eu.eudat.model.deleter.ReferenceDeleter;
import eu.eudat.model.persist.ReferencePersist;
import eu.eudat.model.persist.referencedefinition.DefinitionPersist;
import eu.eudat.model.persist.referencedefinition.FieldPersist;
import eu.eudat.query.ReferenceQuery;
import eu.eudat.query.lookup.ReferenceSearchLookup;
import eu.eudat.service.remotefetcher.RemoteFetcherService;
import eu.eudat.service.remotefetcher.config.entities.SourceBaseConfiguration;
import eu.eudat.service.remotefetcher.criteria.ExternalReferenceCriteria;
import eu.eudat.service.remotefetcher.models.ExternalDataResult;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.exception.MyValidationException;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import jakarta.persistence.EntityManager;
import jakarta.xml.bind.JAXBException;
import org.jetbrains.annotations.NotNull;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class ReferenceServiceImpl implements ReferenceService {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(ReferenceServiceImpl.class));
private final EntityManager entityManager;
private final AuthorizationService authorizationService;
private final DeleterFactory deleterFactory;
private final BuilderFactory builderFactory;
private final ConventionService conventionService;
private final MessageSource messageSource;
private final QueryFactory queryFactory;
private final XmlHandlingService xmlHandlingService;
public final RemoteFetcherService remoteFetcherService;
public ReferenceServiceImpl(
EntityManager entityManager,
AuthorizationService authorizationService,
DeleterFactory deleterFactory,
BuilderFactory builderFactory,
ConventionService conventionService,
MessageSource messageSource,
QueryFactory queryFactory,
XmlHandlingService xmlHandlingService, RemoteFetcherService remoteFetcherService) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
this.builderFactory = builderFactory;
this.conventionService = conventionService;
this.messageSource = messageSource;
this.queryFactory = queryFactory;
this.xmlHandlingService = xmlHandlingService;
this.remoteFetcherService = remoteFetcherService;
}
@Override
public Reference persist(ReferencePersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, JsonProcessingException, TransformerException, ParserConfigurationException {
logger.debug(new MapLogEntry("persisting data").And("model", model).And("fields", fields));
this.authorizationService.authorizeForce(Permission.EditDmpBlueprint);
Boolean isUpdate = this.conventionService.isValidGuid(model.getId());
ReferenceEntity data;
if (isUpdate) {
data = this.entityManager.find(ReferenceEntity.class, model.getId());
if (data == null)
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Reference.class.getSimpleName()}, LocaleContextHolder.getLocale()));
} else {
data = new ReferenceEntity();
data.setId(UUID.randomUUID());
data.setIsActive(IsActive.Active);
data.setCreatedAt(Instant.now());
}
data.setLabel(model.getLabel());
data.setTypeId(model.getTypeId());
data.setDescription(model.getDescription());
data.setDefinition(this.xmlHandlingService.toXmlSafe(this.buildDefinitionEntity(model.getDefinition())));
data.setUpdatedAt(Instant.now());
data.setReference(model.getReference());
data.setAbbreviation(model.getAbbreviation());
data.setSource(model.getSource());
data.setSourceType(model.getSourceType());
if (isUpdate) this.entityManager.merge(data);
else this.entityManager.persist(data);
this.entityManager.flush();
return this.builderFactory.builder(ReferenceBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, Reference._id), data);
}
private @NotNull DefinitionEntity buildDefinitionEntity(DefinitionPersist persist){
DefinitionEntity data = new DefinitionEntity();
if (persist == null) return data;
if (!this.conventionService.isListNullOrEmpty(persist.getFields())){
data.setFields(new ArrayList<>());
for (FieldPersist fieldPersist: persist.getFields()) {
data.getFields().add(this.buildFieldEntity(fieldPersist));
}
}
return data;
}
private @NotNull FieldEntity buildFieldEntity(FieldPersist persist){
FieldEntity data = new FieldEntity();
if (persist == null) return data;
data.setCode(persist.getCode());
data.setDataType(persist.getDataType());
data.setValue(persist.getValue());
return data;
}
@Override
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException {
logger.debug("deleting : {}", id);
this.authorizationService.authorizeForce(Permission.DeleteReference);
this.deleterFactory.deleter(ReferenceDeleter.class).deleteAndSaveByIds(List.of(id));
}
@Override
public List<Reference> searchReferenceData(ReferenceSearchLookup lookup) throws MyNotFoundException {
int initialOffset = 0;
if (lookup.getPage() != null && !lookup.getPage().isEmpty()){
initialOffset = lookup.getPage().getOffset();
lookup.getPage().setOffset(0);
}
ReferenceTypeEntity data = this.entityManager.find(ReferenceTypeEntity.class, lookup.getTypeId());
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{lookup.getTypeId(), ReferenceType.class.getSimpleName()}, LocaleContextHolder.getLocale()));
String like;
if (lookup.getLike() != null){
like = lookup.getLike().replaceAll("%", "");
} else {
like = null;
}
ExternalReferenceCriteria externalReferenceCriteria = new ExternalReferenceCriteria(like);
ExternalDataResult remoteRepos = this.getReferenceData(data, externalReferenceCriteria, lookup.getKey());
List<Reference> externalModels = new ArrayList<>();
if (remoteRepos != null && !this.conventionService.isListNullOrEmpty(remoteRepos.getResults())){
List<ReferenceEntity> referenceEntities = new ArrayList<>();
for (Map<String, String> result : remoteRepos.getResults()){
if (result == null || result.isEmpty()) continue;;
ReferenceEntity referenceEntity = buildReferenceEntityFromExternalData(result, data, remoteRepos);
referenceEntities.add(referenceEntity);
}
externalModels = this.builderFactory.builder(ReferenceBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(lookup.getProject(), referenceEntities);
}
List<Reference> models = this.fetchReferenceFromDb(lookup);
for (Reference externalReference : externalModels){
if (models.stream().noneMatch(x-> x.getReference() != null && x.getSource() != null && x.getReference().equals(externalReference.getReference()) && x.getSource().equals(externalReference.getSource()))) models.add(externalReference);
}
if (!this.conventionService.isNullOrEmpty(like)) { models = models.stream().filter(x -> x.getLabel().toLowerCase().contains(like.toLowerCase())).collect(Collectors.toList()); }
models = models.stream().sorted(Comparator.comparing(Reference::getLabel, Comparator.nullsFirst(Comparator.naturalOrder()))).collect(Collectors.toList());
if (lookup.getPage() != null && !lookup.getPage().isEmpty()){
models = models.stream().skip(initialOffset).limit(lookup.getPage().getSize()).toList();
}
return models;
}
@NotNull
private ReferenceEntity buildReferenceEntityFromExternalData(Map<String, String> result, ReferenceTypeEntity data, ExternalDataResult remoteRepos) {
ReferenceEntity referenceEntity = new ReferenceEntity();
referenceEntity.setTypeId(data.getId());
referenceEntity.setIsActive(IsActive.Active);
referenceEntity.setReference(result.getOrDefault(ReferenceEntity.KnownFields.ReferenceId, null));
referenceEntity.setSource(result.getOrDefault(ReferenceEntity.KnownFields.Key, null));
referenceEntity.setAbbreviation(result.getOrDefault(ReferenceEntity.KnownFields.Abbreviation, null));
referenceEntity.setDescription(result.getOrDefault(ReferenceEntity.KnownFields.Description, null));
referenceEntity.setLabel(result.getOrDefault(ReferenceEntity.KnownFields.Label, null));
referenceEntity.setSourceType(ReferenceSourceType.External);
DefinitionEntity definitionEntity = new DefinitionEntity();
definitionEntity.setFields(new ArrayList<>());
for (Map.Entry<String, String> resultValue : result.entrySet()){
FieldEntity fieldEntity = new FieldEntity();
fieldEntity.setCode(resultValue.getKey());
fieldEntity.setValue(resultValue.getValue());
fieldEntity.setDataType(ReferenceFieldDataType.Text);
definitionEntity.getFields().add(fieldEntity);
}
referenceEntity.setDefinition(this.xmlHandlingService.toXmlSafe(definitionEntity));
return referenceEntity;
}
private List<Reference> fetchReferenceFromDb(ReferenceSearchLookup lookup){
ReferenceQuery query = lookup.enrich(this.queryFactory).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).sourceTypes(ReferenceSourceType.Internal).typeIds(lookup.getTypeId());
//TODO: Stratis update db for references.
//List<ReferenceEntity> data = query.collectAs(lookup.getProject());
//return this.builderFactory.builder(ReferenceBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(lookup.getProject(), data);
return new ArrayList<>();
}
private ExternalDataResult getReferenceData(ReferenceTypeEntity referenceType, ExternalReferenceCriteria externalReferenceCriteria, String key) {
ReferenceTypeDefinitionEntity referenceTypeDefinition = this.xmlHandlingService.fromXmlSafe(ReferenceTypeDefinitionEntity.class, referenceType.getDefinition());
if (referenceTypeDefinition == null || this.conventionService.isListNullOrEmpty(referenceTypeDefinition.getSources())) return new ExternalDataResult();
ExternalDataResult results = this.remoteFetcherService.getExternalData(referenceTypeDefinition.getSources().stream().map(x -> (SourceBaseConfiguration)x).collect(Collectors.toList()), externalReferenceCriteria, key);
for (Map<String, String> result: results.getResults()) {
result.put("referenceType", referenceType.getName());
}
return results;
}
// private List<Map<String, String>> getAll ( List<ReferenceTypeSourceBaseConfigurationEntity> sources, ReferenceDefinitionSearchLookup lookup){
// List<Map<String, String>> results = new LinkedList<>();
//
// if (sources == null || sources.isEmpty()) {
// return results;
// }
//
// sources.sort(Comparator.comparing(ReferenceTypeSourceBaseConfigurationEntity::getOrdinal));
//
// List<ReferenceTypeSourceExternalApiConfigurationEntity> apiSources = sources.stream().filter(x-> ReferenceTypeSourceType.API.equals(x.getType())).map(x-> (ReferenceTypeSourceExternalApiConfigurationEntity)x).toList();
// apiSources.forEach(source -> {
// try {
// String auth = null;
// if (source.getAuth()!= null) {
// //auth = this.getAuthentication(source.getAuth());
// }
// results.addAll(getAllApiResultsFromUrl(source.getUrl(), null, source.getResults(), source.getPaginationPath(), lookup, source.getLabel(), source.getKey(), source.getContentType(), source.getFirstPage(), source.getRequestBody(), source.getHttpMethod(), source.getFilterType(), source.getQueries(), auth));
// } catch (Exception e) {
// logger.error(e.getLocalizedMessage(), e);
// }
// });
// List<ReferenceTypeSourceStaticOptionConfigurationEntity> staticSources = sources.stream().filter(x-> ReferenceTypeSourceType.STATIC.equals(x.getType())).map(x-> (ReferenceTypeSourceStaticOptionConfigurationEntity)x).toList();
// staticSources.forEach(source -> {
// Map<String, String> map = new HashMap<>();
// source.getOptions().forEach(option -> {
// map.put(option.getCode(), option.getValue());
// map.put("tag", source.getLabel());
// map.put("key", source.getKey());
// });
// results.add(map);
// });
//
// return results;
// }
// private String getAuthentication(AuthenticationConfigurationEntity authenticationConfiguration) {
// HttpMethod method = HttpMethod.valueOf(authenticationConfiguration.getAuthMethod().name());
// Map<String, Object> response = this.client.method(method).uri(authenticationConfiguration.getAuthUrl())
// .contentType(MediaType.APPLICATION_JSON)
// .bodyValue(this.parseBodyString(authenticationConfiguration.getAuthRequestBody()))
// .exchangeToMono(mono -> mono.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
// })).block();
//
//
//
// return authenticationConfiguration.getType() + " " + response.get(authenticationConfiguration.getAuthTokenPath());
// }
// private String parseBodyString(String bodyString) {
// String finalBodyString = bodyString;
// if (bodyString.contains("{env:")) {
// int index = bodyString.indexOf("{env: ");
// while (index >= 0) {
// int endIndex = bodyString.indexOf("}", index + 6);
// String envName = bodyString.substring(index + 6, endIndex);
// finalBodyString = finalBodyString.replace("{env: " + envName + "}", System.getenv(envName));
// index = bodyString.indexOf("{env: ", index + 6);
// }
// }
// return finalBodyString;
// }
// private List<Map<String, String>> getAllApiResultsFromUrl(String urlPath, FetchStrategy fetchStrategy, final ResultsConfigurationEntity resultsEntity, final String jsonPaginationPath, ReferenceDefinitionSearchLookup lookup, String label, String key, String contentType, String firstPage, String requestBody, ReferenceTypeExternalApiHTTPMethodType requestType, String filterType, List<QueryConfigEntity> queries, String auth) throws Exception {
// Set<Integer> pages = new HashSet<>();
//
// String replacedUrlPath = replaceLookupFields(urlPath, lookup, firstPage, queries);
// String replacedUrlBody = replaceLookupFields(requestBody, lookup, firstPage, queries);
//
// ExternalDataResult externalRefernceResult = getResultsFromUrl(replacedUrlPath, resultsEntity, jsonPaginationPath, contentType, replacedUrlBody, requestType, auth);
// if(externalRefernceResult != null) {
// if (filterType != null && filterType.equals("local") && (lookup.getLike() != null && !lookup.getLike().isEmpty())) {
// externalRefernceResult.setResults(externalRefernceResult.getResults().stream()
// .filter(r -> r.get("name").toLowerCase().contains(lookup.getLike().toLowerCase()))
// .collect(Collectors.toList()));
// }
// if (fetchStrategy == FetchStrategy.FIRST)
// return externalRefernceResult.getResults().stream().peek(x -> x.put("tag", label)).peek(x -> x.put("key", key)).collect(Collectors.toList());
//
// //Long maxResults = configLoader.getExternalUrls().getMaxresults();
// Long maxResults = Long.valueOf(1000);
// if (externalRefernceResult.getResults().size() > maxResults)
// throw new HugeResultSetException("The submitted search query " + lookup.getLike() + " is about to return " + externalRefernceResult.getResults().size() + " results... Please submit a more detailed search query");
//
// Optional<ExternalDataResult> optionalResults = pages.parallelStream()
// .map(page -> getResultsFromUrl(urlPath + "&page=" + page, resultsEntity, jsonPaginationPath, contentType, replacedUrlBody, requestType, auth))
// .filter(Objects::nonNull)
// .reduce((result1, result2) -> {
// result1.getResults().addAll(result2.getResults());
// return result1;
// });
// ExternalDataResult remainingExternalDataResult = optionalResults.orElseGet(ExternalDataResult::new);
// remainingExternalDataResult.getResults().addAll(externalRefernceResult.getResults());
//
// return remainingExternalDataResult.getResults().stream().peek(x -> x.put("tag", label)).peek(x -> x.put("key", key)).collect(Collectors.toList());
// }
// else {
// return new LinkedList<>();
// }
// }
// private String replaceLookupFields(String urlPath, ReferenceDefinitionSearchLookup lookup, String firstPage, List<QueryConfigEntity> queries){
// String completedPath = urlPath;
//
// if (urlPath.contains("openaire") || urlPath.contains("orcid") ){
// if (lookup.getLike() != null) {
// completedPath = completedPath.replace("{like}", lookup.getLike());
// } else {
// completedPath = completedPath.replace("{like}", "*");
// }
// }
//
// if (urlPath.contains("{like}")){
// if (lookup.getLike() != null) {
// completedPath = completedPath.replace("{like}", lookup.getLike());
// } else {
// completedPath = completedPath.replace("{like}", "");
// }
// }
//
// if (urlPath.contains("{page}")) {
// if (lookup.getPage() != null && lookup.getPage().getOffset() > 0) {
// completedPath = completedPath.replace("{page}", String.valueOf(lookup.getPage().getOffset()));
// } else if (firstPage != null) {
// completedPath = completedPath.replace("{page}", firstPage);
// } else {
// completedPath = completedPath.replace("{page}", "1");
// }
// }
//
// if (urlPath.contains("{pageSize}")){
// if (lookup.getPage() != null && lookup.getPage().getSize() > 0) {
// completedPath = completedPath.replace("{pageSize}", String.valueOf(lookup.getPage().getSize()));
// } else {
// completedPath = completedPath.replace("{pageSize}", "100");
// }
// }
//
//
// return completedPath;
// }
// protected ExternalDataResult getResultsFromUrl(String urlString, ResultsConfigurationEntity resultsEntity, String jsonPaginationPath, String contentType, String requestBody, ReferenceTypeExternalApiHTTPMethodType httpMethod, String auth) {
//
// try {
// ResponseEntity<String> response;
// JsonNode jsonBody = new ObjectMapper().readTree(requestBody);
//
// response = this.client.method(HttpMethod.valueOf(httpMethod.name())).uri(urlString).headers(httpHeaders -> {
// if (contentType != null && !contentType.isEmpty()) {
// httpHeaders.setAccept(Collections.singletonList(MediaType.valueOf(contentType)));
// httpHeaders.setContentType(MediaType.valueOf(contentType));
// }
// if (auth != null) {
// httpHeaders.set("Authorization", auth);
// }
// }).bodyValue(jsonBody).retrieve().toEntity(String.class).block();
// if (response.getStatusCode() == HttpStatus.OK) { // success
// ExternalDataResult externalDataResult = new ExternalDataResult();
// if (response.getHeaders().get("Content-Type").get(0).contains("json")) {
// DocumentContext jsonContext = JsonPath.parse(response.getBody());
// externalDataResult = this.getFromJson(jsonContext, resultsEntity);
// }
//
// return externalDataResult;
// }
// } catch (Exception exception) {
// logger.error(exception.getMessage(), exception);
// }
//
// return null;
// }
// public static ExternalDataResult getFromJson(DocumentContext jsonContext, ResultsConfigurationEntity resultsEntity) {
// return new ExternalDataResult(parseData(jsonContext, resultsEntity));
// }
// private static List<Map<String, String>> parseData (DocumentContext jsonContext, ResultsConfigurationEntity resultsEntity) {
// List <Map<String, String>> rawData = jsonContext.read(resultsEntity.getResultsArrayPath());
// List<Map<String, String>> parsedData = new ArrayList<>();
//
// for (Map<String, String> stringObjectMap: rawData){
// Map<String, String> map = new HashMap<>();
// for(ResultFieldsMappingConfigurationEntity field: resultsEntity.getFieldsMapping()){
// String pathValue = field.getResponsePath();
// if (!pathValue.contains(".")){
// if (stringObjectMap.containsKey(pathValue)) {
// //map.put(field.getCode(), stringObjectMap.get(pathValue));
// map.put(field.getCode(), normalizeValue(stringObjectMap.get(pathValue)));
// }
// }else {
// if (stringObjectMap.containsKey(pathValue.split("\\.")[0])){
// String value = null;
// Object fieldObj = stringObjectMap.get(pathValue.split("\\.")[0]);
// if (fieldObj != null){
// if (fieldObj instanceof Map){
// Object o = ((Map<String, Object>) fieldObj).get(pathValue.split("\\.")[1]);
// if(o instanceof String){
// value = (String)o;
// }
// else if(o instanceof Integer){
// value = String.valueOf(o);
// }
// } else if (fieldObj instanceof List) {
// Object o = ((List<Map<String,?>>) fieldObj).get(0).get(pathValue.split("\\.")[1]);
// if(o instanceof String){
// value = (String)o;
// }
// else if(o instanceof Integer){
// value = String.valueOf(o);
// }
// }
// }
// if (value != null){
// map.put(field.getCode(), value);
// }
// }
// }
// }
// parsedData.add(map);
// }
// return parsedData;
// }
//
//
// private static String normalizeValue(Object value) {
// if (value instanceof JSONArray) {
// JSONArray jarr = (JSONArray) value;
// if (jarr.get(0) instanceof String) {
// return jarr.get(0).toString();
// } else {
// for (Object o : jarr) {
// if ((o instanceof Map) && ((Map) o).containsKey("content")) {
// try {
// return ((Map<String, String>) o).get("content");
// }
// catch (ClassCastException e){
// if(((Map<?, ?>) o).get("content") instanceof Integer) {
// return String.valueOf(((Map<?, ?>) o).get("content"));
// }
// return null;
// }
// }
// }
// }
// } else if (value instanceof Map) {
// String key = ((Map<String, String>)value).containsKey("$") ? "$" : "content";
// return ((Map<String, String>)value).get(key);
// }
// return value != null ? value.toString() : null;
// }
}