Merge branch 'dmp-refactoring' of https://code-repo.d4science.org/MaDgiK-CITE/argos into dmp-refactoring

This commit is contained in:
Sofia Papacharalampous 2024-04-11 17:46:58 +03:00
commit 928babb9d5
18 changed files with 457 additions and 135 deletions

View File

@ -114,6 +114,7 @@ public class AuditableAction {
public static final EventId StorageFile_Download = new EventId(14000, "StorageFile_Download"); public static final EventId StorageFile_Download = new EventId(14000, "StorageFile_Download");
public static final EventId StorageFile_Upload = new EventId(14001, "StorageFile_Upload"); public static final EventId StorageFile_Upload = new EventId(14001, "StorageFile_Upload");
public static final EventId StorageFile_Query = new EventId(14002, "StorageFile_Query");
public static final EventId Dashboard_MyRecentActivityItems = new EventId(15000, "Dashboard_MyRecentActivityItems"); public static final EventId Dashboard_MyRecentActivityItems = new EventId(15000, "Dashboard_MyRecentActivityItems");
public static final EventId Dashboard_MyDashboardStatistics = new EventId(15001, "Dashboard_MyDashboardStatistics"); public static final EventId Dashboard_MyDashboardStatistics = new EventId(15001, "Dashboard_MyDashboardStatistics");

View File

@ -1,4 +1,4 @@
package eu.eudat.types; package eu.eudat.commons.metrics;
public class MetricNames { public class MetricNames {
public static final String DATASET_TEMPLATE = "argos_dmp_templates"; public static final String DATASET_TEMPLATE = "argos_dmp_templates";

View File

@ -3,6 +3,7 @@ package eu.eudat.model.builder.descriptionpropertiesdefinition;
import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.commons.enums.FieldType; import eu.eudat.commons.enums.FieldType;
import eu.eudat.commons.types.description.FieldEntity; import eu.eudat.commons.types.description.FieldEntity;
import eu.eudat.commons.types.descriptiontemplate.fielddata.LabelAndMultiplicityDataEntity;
import eu.eudat.commons.types.descriptiontemplate.fielddata.SelectDataEntity; import eu.eudat.commons.types.descriptiontemplate.fielddata.SelectDataEntity;
import eu.eudat.convention.ConventionService; import eu.eudat.convention.ConventionService;
import eu.eudat.model.Reference; import eu.eudat.model.Reference;
@ -72,12 +73,16 @@ public class FieldBuilder extends BaseBuilder<Field, FieldEntity> {
if (fields.hasField(this.asIndexer(Field._dateValue)) && FieldType.isDateType(fieldType)) m.setDateValue(d.getDateValue()); if (fields.hasField(this.asIndexer(Field._dateValue)) && FieldType.isDateType(fieldType)) m.setDateValue(d.getDateValue());
if (fields.hasField(this.asIndexer(Field._textValue)) && FieldType.isTextType(fieldType)) m.setTextValue(d.getTextValue()); if (fields.hasField(this.asIndexer(Field._textValue)) && FieldType.isTextType(fieldType)) m.setTextValue(d.getTextValue());
if (fields.hasField(this.asIndexer(Field._textListValue)) && FieldType.isTextListType(fieldType)) { if (fields.hasField(this.asIndexer(Field._textListValue)) && FieldType.isTextListType(fieldType)) {
boolean isSelectMultiSelect = true; boolean isMultiSelect = true;
if(this.fieldEntity != null && this.fieldEntity.getData() != null && this.fieldEntity.getData().getFieldType().equals(FieldType.SELECT) && this.fieldEntity.getData() instanceof SelectDataEntity){ if(this.fieldEntity != null && this.fieldEntity.getData() != null && (this.fieldEntity.getData().getFieldType().equals(FieldType.SELECT) || this.fieldEntity.getData().getFieldType().equals(FieldType.INTERNAL_ENTRIES_DMPS) || this.fieldEntity.getData().getFieldType().equals(FieldType.INTERNAL_ENTRIES_DESCRIPTIONS))){
isSelectMultiSelect = ((SelectDataEntity) this.fieldEntity.getData()).getMultipleSelect(); if (this.fieldEntity.getData() instanceof SelectDataEntity) isMultiSelect = ((SelectDataEntity) this.fieldEntity.getData()).getMultipleSelect();
} else if (this.fieldEntity.getData() instanceof LabelAndMultiplicityDataEntity) isMultiSelect = ((LabelAndMultiplicityDataEntity) this.fieldEntity.getData()).getMultipleSelect();
if (fieldType.equals(FieldType.SELECT) && !isSelectMultiSelect && !this.conventionService.isListNullOrEmpty(d.getTextListValue())){
m.setTextValue(d.getTextListValue().stream().findFirst().orElse(null)); if (!isMultiSelect && !this.conventionService.isListNullOrEmpty(d.getTextListValue())){
m.setTextValue(d.getTextListValue().stream().findFirst().orElse(null));
}else{
m.setTextListValue(d.getTextListValue());
}
} else{ } else{
m.setTextListValue(d.getTextListValue()); m.setTextListValue(d.getTextListValue());
} }

View File

@ -516,22 +516,38 @@ public class DescriptionServiceImpl implements DescriptionService {
} }
} }
else if (FieldType.isTextListType(fieldType)) { else if (FieldType.isTextListType(fieldType)) {
if (FieldType.INTERNAL_ENTRIES_DMPS.equals(fieldType) && !this.conventionService.isListNullOrEmpty(persist.getTextListValue())){ List<UUID> ids = new ArrayList<>();
List<UUID> ids = persist.getTextListValue().stream().map(UUID::fromString).toList(); if (FieldType.INTERNAL_ENTRIES_DMPS.equals(fieldType)) {
Set<UUID> existingIds = this.queryFactory.query(DmpQuery.class).ids(ids).isActive(IsActive.Active).collectAs(new BaseFieldSet().ensure(Dmp._id)).stream().map(DmpEntity::getId).collect(Collectors.toSet()); if (!this.conventionService.isListNullOrEmpty(persist.getTextListValue())) persist.getTextListValue().stream().map(UUID::fromString).toList();
for (UUID id : ids){ else if (!this.conventionService.isNullOrEmpty(persist.getTextValue())) ids.add(UUID.fromString(persist.getTextValue()));
if (!existingIds.contains(id)) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (ids.size() > 0){
Set<UUID> existingIds = this.queryFactory.query(DmpQuery.class).ids(ids).isActive(IsActive.Active).collectAs(new BaseFieldSet().ensure(Dmp._id)).stream().map(DmpEntity::getId).collect(Collectors.toSet());
for (UUID id : ids){
if (!existingIds.contains(id)) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
}
} }
} if (FieldType.INTERNAL_ENTRIES_DESCRIPTIONS.equals(fieldType) && !this.conventionService.isListNullOrEmpty(persist.getTextListValue())){
List<UUID> ids = persist.getTextListValue().stream().map(UUID::fromString).toList(); } if (FieldType.INTERNAL_ENTRIES_DESCRIPTIONS.equals(fieldType)){
Set<UUID> existingIds = this.queryFactory.query(DescriptionQuery.class).ids(ids).isActive(IsActive.Active).collectAs(new BaseFieldSet().ensure(Description._id)).stream().map(DescriptionEntity::getId).collect(Collectors.toSet()); if ( !this.conventionService.isListNullOrEmpty(persist.getTextListValue())) ids = persist.getTextListValue().stream().map(UUID::fromString).toList();
for (UUID id : ids){ else if (!this.conventionService.isNullOrEmpty(persist.getTextValue())) ids.add(UUID.fromString(persist.getTextValue()));
if (!existingIds.contains(id)) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Description.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (ids.size() > 0) {
Set<UUID> existingIds = this.queryFactory.query(DescriptionQuery.class).ids(ids).isActive(IsActive.Active).collectAs(new BaseFieldSet().ensure(Description._id)).stream().map(DescriptionEntity::getId).collect(Collectors.toSet());
for (UUID id : ids){
if (!existingIds.contains(id)) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Description.class.getSimpleName()}, LocaleContextHolder.getLocale()));
}
} }
} }
if (!ids.isEmpty()){
if (ids.size() > 1) data.setTextListValue(persist.getTextListValue());
else data.setTextListValue(List.of(persist.getTextValue()));
}else{
data.setTextListValue(persist.getTextListValue());
}
if (fieldType.equals(FieldType.SELECT) && this.conventionService.isListNullOrEmpty(persist.getTextListValue()) && !this.conventionService.isNullOrEmpty(persist.getTextValue())){ if (fieldType.equals(FieldType.SELECT) && this.conventionService.isListNullOrEmpty(persist.getTextListValue()) && !this.conventionService.isNullOrEmpty(persist.getTextValue())){
data.setTextListValue(List.of(persist.getTextValue())); data.setTextListValue(List.of(persist.getTextValue()));
} else{ } else if (fieldType.equals(FieldType.SELECT) || fieldType.equals(FieldType.TAGS)){
data.setTextListValue(persist.getTextListValue()); data.setTextListValue(persist.getTextListValue());
} }
} }

View File

@ -0,0 +1,11 @@
package eu.eudat.service.metrics;
import io.prometheus.client.Gauge;
import javax.management.InvalidApplicationException;
import java.util.Map;
public interface MetricsService {
void calculate(Map<String, Gauge> gauges) throws InvalidApplicationException;
Map<String, Gauge> gaugesBuild();
}

View File

@ -0,0 +1,92 @@
package eu.eudat.service.metrics;
import eu.eudat.commons.enums.DmpStatus;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.metrics.MetricNames;
import eu.eudat.data.TenantEntityManager;
import eu.eudat.query.DmpQuery;
import gr.cite.tools.data.query.QueryFactory;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.Gauge;
import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Service
public class MetricsServiceImpl implements MetricsService {
private final PrometheusMeterRegistry registry;
private final QueryFactory queryFactory;
private final TenantEntityManager entityManager;
public MetricsServiceImpl(PrometheusMeterRegistry registry, QueryFactory queryFactory, TenantEntityManager entityManager) {
this.registry = registry;
this.queryFactory = queryFactory;
this.entityManager = entityManager;
}
@Override
public void calculate(Map<String, Gauge> gauges) throws InvalidApplicationException {
try {
this.entityManager.disableTenantFilters();
this.setGauseValue(gauges, MetricNames.DMP, calculateFinalizedDmps(), MetricNames.FINALIZED);
}finally {
this.entityManager.enableTenantFilters();
}
}
@Override
public Map<String, Gauge> gaugesBuild() {
registry.clear();
return Stream.of( new Object[][]{
{MetricNames.DMP, Gauge.build().name(MetricNames.DMP).help("Number of managed DMPs").labelNames("status").register(registry.getPrometheusRegistry())},
{MetricNames.NEXUS + MetricNames.DMP, Gauge.build().name(MetricNames.NEXUS + MetricNames.DMP).help("Number of managed DMPs during Nexus").labelNames("status").register(registry.getPrometheusRegistry())},
{MetricNames.FUNDERS, Gauge.build().name(MetricNames.FUNDERS).help("Number of registered Funders").register(registry.getPrometheusRegistry())},
{MetricNames.GRANTS, Gauge.build().name(MetricNames.GRANTS).help("Number of registered Grants").register(registry.getPrometheusRegistry())},
{MetricNames.PROJECT, Gauge.build().name(MetricNames.PROJECT).help("Number of registered Projects").register(registry.getPrometheusRegistry())},
{MetricNames.RESEARCHER, Gauge.build().name(MetricNames.RESEARCHER).help("Number of Colaborators/Researchers").register(registry.getPrometheusRegistry())},
{MetricNames.NEXUS + MetricNames.FUNDERS, Gauge.build().name(MetricNames.NEXUS + MetricNames.FUNDERS).help("Number of registered Funders during Nexus").register(registry.getPrometheusRegistry())},
{MetricNames.NEXUS + MetricNames.GRANTS, Gauge.build().name(MetricNames.NEXUS + MetricNames.GRANTS).help("Number of registered Grants during Nexus").register(registry.getPrometheusRegistry())},
{MetricNames.NEXUS + MetricNames.PROJECT, Gauge.build().name(MetricNames.NEXUS + MetricNames.PROJECT).help("Number of registered Projects during Nexus").register(registry.getPrometheusRegistry())},
{MetricNames.NEXUS + MetricNames.RESEARCHER, Gauge.build().name(MetricNames.NEXUS + MetricNames.RESEARCHER).help("Number of Colaborators/Researchers during Nexus").register(registry.getPrometheusRegistry())},
{MetricNames.DATASET, Gauge.build().name(MetricNames.DATASET).help("Number of managed Dataset Descriptions").labelNames("status").register(registry.getPrometheusRegistry())},
{MetricNames.NEXUS + MetricNames.DATASET, Gauge.build().name(MetricNames.NEXUS + MetricNames.DATASET).help("Number of managed Dataset Descriptions during Nexus").labelNames("status").register(registry.getPrometheusRegistry())},
{MetricNames.DATASET_TEMPLATE, Gauge.build().name(MetricNames.DATASET_TEMPLATE).help("Number of dataset Templates").labelNames("status").register(registry.getPrometheusRegistry())},
{MetricNames.NEXUS + MetricNames.DATASET_TEMPLATE, Gauge.build().name(MetricNames.NEXUS + MetricNames.DATASET_TEMPLATE).help("Number of dataset Templates during Nexus").labelNames("status").register(registry.getPrometheusRegistry())},
{MetricNames.USERS, Gauge.build().name(MetricNames.USERS).help("Number of users").labelNames("type").register(registry.getPrometheusRegistry())},
{MetricNames.LANGUAGES, Gauge.build().name(MetricNames.LANGUAGES).help("Number of Languages").register(registry.getPrometheusRegistry())},
{MetricNames.DMP_WITH_GRANT, Gauge.build().name(MetricNames.DMP_WITH_GRANT).help("Number of Grants based on the status of the DMP that is using them").labelNames("status").register(registry.getPrometheusRegistry())},
{MetricNames.NEXUS + MetricNames.DMP_WITH_GRANT, Gauge.build().name(MetricNames.NEXUS + MetricNames.DMP_WITH_GRANT).help("Number of Grants based on the status of the DMP that is using them during Nexus").labelNames("status").register(registry.getPrometheusRegistry())},
{MetricNames.INSTALLATIONS, Gauge.build().name(MetricNames.INSTALLATIONS).help("Number of Installations").register(registry.getPrometheusRegistry())},
{MetricNames.NEXUS + MetricNames.INSTALLATIONS, Gauge.build().name(MetricNames.NEXUS + MetricNames.INSTALLATIONS).help("Number of Installations").register(registry.getPrometheusRegistry())},
}).collect(Collectors.toMap(data -> (String)data[0], data -> (Gauge) data[1]));
}
private double calculateFinalizedDmps(){
return this.queryFactory.query(DmpQuery.class).statuses(DmpStatus.Finalized).isActive(IsActive.Active).count();
}
private void setGauseValue(Map<String, Gauge> gauges, String name, Double amount, String label) {
if(label != null) {
gauges.get(name).labels(label).set(amount);
} else {
gauges.get(name).set(amount);
}
}
}

View File

@ -0,0 +1,130 @@
package eu.eudat.service.metrics;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.fake.FakeRequestScope;
import eu.eudat.commons.metrics.MetricNames;
import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.data.QueueInboxEntity;
import eu.eudat.data.StorageFileEntity;
import eu.eudat.data.TenantEntity;
import eu.eudat.data.TenantEntityManager;
import eu.eudat.integrationevent.inbox.EventProcessingStatus;
import eu.eudat.model.StorageFile;
import eu.eudat.model.Tenant;
import eu.eudat.query.QueueInboxQuery;
import eu.eudat.query.StorageFileQuery;
import eu.eudat.query.TenantQuery;
import eu.eudat.service.storage.StorageFileService;
import gr.cite.queueinbox.entity.QueueInboxStatus;
import gr.cite.queueinbox.repository.CandidateInfo;
import gr.cite.tools.data.query.Ordering;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.Gauge;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.OptimisticLockException;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import java.io.Closeable;
import java.io.IOException;
import java.time.Instant;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class UpdateMetricsTask implements Closeable, ApplicationListener<ApplicationReadyEvent> {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UpdateMetricsTask.class));
private final UpdateMetricsTaskProperties _config;
private final ApplicationContext applicationContext;
private ScheduledExecutorService scheduler;
private Map<String, Gauge> gauges;
public UpdateMetricsTask(
UpdateMetricsTaskProperties config,
ApplicationContext applicationContext) {
this._config = config;
this.applicationContext = applicationContext;
this.gauges = null;
}
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
long intervalSeconds = this._config .getIntervalSeconds();
if (this._config .getEnable() && intervalSeconds > 0) {
logger.info("File clean up run in {} seconds", intervalSeconds);
scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(this::process, 10, intervalSeconds, TimeUnit.SECONDS);
} else {
scheduler = null;
}
}
@Override
public void close() throws IOException {
if (scheduler != null) this.scheduler.close();
}
protected void process() {
if (!this._config.getEnable()) return;
try {
this.ensureGauges();
this.calculate();
} catch (Exception ex) {
logger.error("Problem processing file cleanups. Breaking for next interval", ex);
}
}
private void ensureGauges() {
if (this.gauges != null) return;
try (FakeRequestScope ignored = new FakeRequestScope()) {
MetricsService metricsService = this.applicationContext.getBean(MetricsService.class);
this.gauges = metricsService.gaugesBuild();
} catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
}
}
private void calculate() {
EntityManager entityManager = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
try {
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager();
TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class);
tenantEntityManager.setEntityManager(entityManager);
TenantScope tenantScope = this.applicationContext.getBean(TenantScope.class);
tenantScope.setTenant(null, tenantScope.getDefaultTenantCode());
MetricsService metricsService = this.applicationContext.getBean(MetricsService.class);
metricsService.calculate(this.gauges);
} finally {
if (entityManager != null) entityManager.close();
}
} catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
}
}
}

View File

@ -0,0 +1,9 @@
package eu.eudat.service.metrics;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(UpdateMetricsTaskProperties.class)
public class UpdateMetricsTaskConfiguration {
}

View File

@ -0,0 +1,27 @@
package eu.eudat.service.metrics;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "metrics.task")
public class UpdateMetricsTaskProperties {
private boolean enable;
private int intervalSeconds;
public boolean getEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
public int getIntervalSeconds() {
return intervalSeconds;
}
public void setIntervalSeconds(int intervalSeconds) {
this.intervalSeconds = intervalSeconds;
}
}

View File

@ -1,23 +1,27 @@
package eu.eudat.controllers; package eu.eudat.controllers;
import eu.eudat.audit.AuditableAction; import eu.eudat.audit.AuditableAction;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.Permission; import eu.eudat.authorization.Permission;
import eu.eudat.commons.enums.StorageType; import eu.eudat.commons.enums.StorageType;
import eu.eudat.commons.scope.user.UserScope; import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.convention.ConventionService; import eu.eudat.convention.ConventionService;
import eu.eudat.data.StorageFileEntity; import eu.eudat.data.StorageFileEntity;
import eu.eudat.model.StorageFile; import eu.eudat.model.StorageFile;
import eu.eudat.model.builder.StorageFileBuilder;
import eu.eudat.model.persist.StorageFilePersist; import eu.eudat.model.persist.StorageFilePersist;
import eu.eudat.query.StorageFileQuery; import eu.eudat.query.StorageFileQuery;
import eu.eudat.service.storage.StorageFileProperties; import eu.eudat.service.storage.StorageFileProperties;
import eu.eudat.service.storage.StorageFileService; import eu.eudat.service.storage.StorageFileService;
import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.auditing.AuditService; import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry; import gr.cite.tools.logging.MapLogEntry;
import gr.cite.tools.validation.ValidatorFactory; import gr.cite.tools.validation.ValidatorFactory;
@ -25,8 +29,8 @@ import org.apache.commons.io.FilenameUtils;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -45,6 +49,7 @@ public class StorageFileController {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(StorageFileController.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(StorageFileController.class));
private final AuditService auditService; private final AuditService auditService;
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
private final BuilderFactory builderFactory;
private final MessageSource messageSource; private final MessageSource messageSource;
private final StorageFileService storageFileService; private final StorageFileService storageFileService;
private final StorageFileProperties config; private final StorageFileProperties config;
@ -55,13 +60,14 @@ public class StorageFileController {
public StorageFileController( public StorageFileController(
AuditService auditService, AuditService auditService,
QueryFactory queryFactory, QueryFactory queryFactory,
MessageSource messageSource, BuilderFactory builderFactory, MessageSource messageSource,
StorageFileService storageFileService, StorageFileService storageFileService,
StorageFileProperties config, StorageFileProperties config,
UserScope userScope, UserScope userScope,
AuthorizationService authorizationService, ConventionService conventionService, ValidatorFactory validatorFactory) { AuthorizationService authorizationService, ConventionService conventionService, ValidatorFactory validatorFactory) {
this.auditService = auditService; this.auditService = auditService;
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.builderFactory = builderFactory;
this.messageSource = messageSource; this.messageSource = messageSource;
this.storageFileService = storageFileService; this.storageFileService = storageFileService;
this.config = config; this.config = config;
@ -71,21 +77,23 @@ public class StorageFileController {
this.validatorFactory = validatorFactory; this.validatorFactory = validatorFactory;
} }
@GetMapping("/name/{id}") @GetMapping("{id}")
public String getName(@PathVariable("id") UUID id) throws MyApplicationException, MyForbiddenException, MyNotFoundException { public StorageFile get(@PathVariable("id") UUID id, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
logger.debug(new MapLogEntry("get name" ).And("id", id)); logger.debug(new MapLogEntry("retrieving " + StorageFile.class.getSimpleName()).And("id", id).And("fields", fieldSet));
this.authorizationService.authorizeForce(Permission.BrowseStorageFile); this.authorizationService.authorizeForce(Permission.BrowseStorageFile);
StorageFileEntity storageFile = this.queryFactory.query(StorageFileQuery.class).ids(id).firstAs(new BaseFieldSet().ensure(StorageFile._fullName)); StorageFileQuery query = this.queryFactory.query(StorageFileQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(id);
if (storageFile == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, StorageFile.class.getSimpleName()}, LocaleContextHolder.getLocale())); StorageFile model = this.builderFactory.builder(StorageFileBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(fieldSet, query.firstAs(fieldSet));
if (model == null)
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, StorageFile.class.getSimpleName()}, LocaleContextHolder.getLocale()));
this.auditService.track(AuditableAction.StorageFile_Download, Map.ofEntries( this.auditService.track(AuditableAction.StorageFile_Query, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("id", id) new AbstractMap.SimpleEntry<String, Object>("id", id),
new AbstractMap.SimpleEntry<String, Object>("fields", fieldSet)
)); ));
return model;
return storageFile.getName();
} }
@ -115,8 +123,8 @@ public class StorageFileController {
return addedFiles; return addedFiles;
} }
@GetMapping("{id}") @GetMapping("download/{id}")
public ResponseEntity<ByteArrayResource> get(@PathVariable("id") UUID id) throws MyApplicationException, MyForbiddenException, MyNotFoundException { public ResponseEntity<byte[]> get(@PathVariable("id") UUID id) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
logger.debug(new MapLogEntry("download" ).And("id", id)); logger.debug(new MapLogEntry("download" ).And("id", id));
this.authorizationService.authorizeForce(Permission.BrowseStorageFile); this.authorizationService.authorizeForce(Permission.BrowseStorageFile);
@ -134,10 +142,14 @@ public class StorageFileController {
String contentType = storageFile.getMimeType(); String contentType = storageFile.getMimeType();
if (this.conventionService.isNullOrEmpty(contentType)) contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE; if (this.conventionService.isNullOrEmpty(contentType)) contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
return ResponseEntity.ok() HttpHeaders responseHeaders = new HttpHeaders();
.contentType(MediaType.valueOf(contentType)) responseHeaders.setContentLength(file.length);
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + storageFile.getName() + (storageFile.getExtension().startsWith(".") ? "" : ".") + storageFile.getExtension() + "\"") responseHeaders.setContentType(MediaType.valueOf(contentType));
.body(new ByteArrayResource(file)); responseHeaders.set("Content-Disposition", "attachment; filename=\"" + storageFile.getName() + (storageFile.getExtension().startsWith(".") ? "" : ".") + storageFile.getExtension() + "\"");
responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition");
responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type");
return new ResponseEntity<>(file, responseHeaders, HttpStatus.OK);
} }
} }

View File

@ -1,7 +1,7 @@
package eu.eudat.logic.managers; package eu.eudat.logic.managers;
import eu.eudat.commons.enums.DescriptionTemplateStatus; import eu.eudat.commons.enums.DescriptionTemplateStatus;
import eu.eudat.types.MetricNames; import eu.eudat.commons.metrics.MetricNames;
import io.micrometer.prometheus.PrometheusMeterRegistry; import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.Gauge; import io.prometheus.client.Gauge;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
@ -75,37 +75,37 @@ public class MetricsManager {
public MetricsManager(PrometheusMeterRegistry registry) { public MetricsManager(PrometheusMeterRegistry registry) {
registry.clear(); registry.clear();
this.gauges = Stream.of( new Object[][]{ this.gauges = Stream.of( new Object[][]{
{MetricNames.DMP, Gauge.build().name(MetricNames.DMP).help("Number of managed DMPs").labelNames("status").register(registry.getPrometheusRegistry())}, // {MetricNames.DMP, Gauge.build().name(MetricNames.DMP).help("Number of managed DMPs").labelNames("status").register(registry.getPrometheusRegistry())},
{MetricNames.NEXUS + MetricNames.DMP, Gauge.build().name(MetricNames.NEXUS + MetricNames.DMP).help("Number of managed DMPs during Nexus").labelNames("status").register(registry.getPrometheusRegistry())}, // {MetricNames.NEXUS + MetricNames.DMP, Gauge.build().name(MetricNames.NEXUS + MetricNames.DMP).help("Number of managed DMPs during Nexus").labelNames("status").register(registry.getPrometheusRegistry())},
//
{MetricNames.FUNDERS, Gauge.build().name(MetricNames.FUNDERS).help("Number of registered Funders").register(registry.getPrometheusRegistry())}, // {MetricNames.FUNDERS, Gauge.build().name(MetricNames.FUNDERS).help("Number of registered Funders").register(registry.getPrometheusRegistry())},
{MetricNames.GRANTS, Gauge.build().name(MetricNames.GRANTS).help("Number of registered Grants").register(registry.getPrometheusRegistry())}, // {MetricNames.GRANTS, Gauge.build().name(MetricNames.GRANTS).help("Number of registered Grants").register(registry.getPrometheusRegistry())},
{MetricNames.PROJECT, Gauge.build().name(MetricNames.PROJECT).help("Number of registered Projects").register(registry.getPrometheusRegistry())}, // {MetricNames.PROJECT, Gauge.build().name(MetricNames.PROJECT).help("Number of registered Projects").register(registry.getPrometheusRegistry())},
{MetricNames.RESEARCHER, Gauge.build().name(MetricNames.RESEARCHER).help("Number of Colaborators/Researchers").register(registry.getPrometheusRegistry())}, // {MetricNames.RESEARCHER, Gauge.build().name(MetricNames.RESEARCHER).help("Number of Colaborators/Researchers").register(registry.getPrometheusRegistry())},
//
{MetricNames.NEXUS + MetricNames.FUNDERS, Gauge.build().name(MetricNames.NEXUS + MetricNames.FUNDERS).help("Number of registered Funders during Nexus").register(registry.getPrometheusRegistry())}, // {MetricNames.NEXUS + MetricNames.FUNDERS, Gauge.build().name(MetricNames.NEXUS + MetricNames.FUNDERS).help("Number of registered Funders during Nexus").register(registry.getPrometheusRegistry())},
{MetricNames.NEXUS + MetricNames.GRANTS, Gauge.build().name(MetricNames.NEXUS + MetricNames.GRANTS).help("Number of registered Grants during Nexus").register(registry.getPrometheusRegistry())}, // {MetricNames.NEXUS + MetricNames.GRANTS, Gauge.build().name(MetricNames.NEXUS + MetricNames.GRANTS).help("Number of registered Grants during Nexus").register(registry.getPrometheusRegistry())},
{MetricNames.NEXUS + MetricNames.PROJECT, Gauge.build().name(MetricNames.NEXUS + MetricNames.PROJECT).help("Number of registered Projects during Nexus").register(registry.getPrometheusRegistry())}, // {MetricNames.NEXUS + MetricNames.PROJECT, Gauge.build().name(MetricNames.NEXUS + MetricNames.PROJECT).help("Number of registered Projects during Nexus").register(registry.getPrometheusRegistry())},
{MetricNames.NEXUS + MetricNames.RESEARCHER, Gauge.build().name(MetricNames.NEXUS + MetricNames.RESEARCHER).help("Number of Colaborators/Researchers during Nexus").register(registry.getPrometheusRegistry())}, // {MetricNames.NEXUS + MetricNames.RESEARCHER, Gauge.build().name(MetricNames.NEXUS + MetricNames.RESEARCHER).help("Number of Colaborators/Researchers during Nexus").register(registry.getPrometheusRegistry())},
//
{MetricNames.DATASET, Gauge.build().name(MetricNames.DATASET).help("Number of managed Dataset Descriptions").labelNames("status").register(registry.getPrometheusRegistry())}, // {MetricNames.DATASET, Gauge.build().name(MetricNames.DATASET).help("Number of managed Dataset Descriptions").labelNames("status").register(registry.getPrometheusRegistry())},
//
{MetricNames.NEXUS + MetricNames.DATASET, Gauge.build().name(MetricNames.NEXUS + MetricNames.DATASET).help("Number of managed Dataset Descriptions during Nexus").labelNames("status").register(registry.getPrometheusRegistry())}, // {MetricNames.NEXUS + MetricNames.DATASET, Gauge.build().name(MetricNames.NEXUS + MetricNames.DATASET).help("Number of managed Dataset Descriptions during Nexus").labelNames("status").register(registry.getPrometheusRegistry())},
//
{MetricNames.DATASET_TEMPLATE, Gauge.build().name(MetricNames.DATASET_TEMPLATE).help("Number of dataset Templates").labelNames("status").register(registry.getPrometheusRegistry())}, // {MetricNames.DATASET_TEMPLATE, Gauge.build().name(MetricNames.DATASET_TEMPLATE).help("Number of dataset Templates").labelNames("status").register(registry.getPrometheusRegistry())},
//
{MetricNames.NEXUS + MetricNames.DATASET_TEMPLATE, Gauge.build().name(MetricNames.NEXUS + MetricNames.DATASET_TEMPLATE).help("Number of dataset Templates during Nexus").labelNames("status").register(registry.getPrometheusRegistry())}, // {MetricNames.NEXUS + MetricNames.DATASET_TEMPLATE, Gauge.build().name(MetricNames.NEXUS + MetricNames.DATASET_TEMPLATE).help("Number of dataset Templates during Nexus").labelNames("status").register(registry.getPrometheusRegistry())},
//
{MetricNames.USERS, Gauge.build().name(MetricNames.USERS).help("Number of users").labelNames("type").register(registry.getPrometheusRegistry())}, // {MetricNames.USERS, Gauge.build().name(MetricNames.USERS).help("Number of users").labelNames("type").register(registry.getPrometheusRegistry())},
//
{MetricNames.LANGUAGES, Gauge.build().name(MetricNames.LANGUAGES).help("Number of Languages").register(registry.getPrometheusRegistry())}, // {MetricNames.LANGUAGES, Gauge.build().name(MetricNames.LANGUAGES).help("Number of Languages").register(registry.getPrometheusRegistry())},
//
{MetricNames.DMP_WITH_GRANT, Gauge.build().name(MetricNames.DMP_WITH_GRANT).help("Number of Grants based on the status of the DMP that is using them").labelNames("status").register(registry.getPrometheusRegistry())}, // {MetricNames.DMP_WITH_GRANT, Gauge.build().name(MetricNames.DMP_WITH_GRANT).help("Number of Grants based on the status of the DMP that is using them").labelNames("status").register(registry.getPrometheusRegistry())},
//
{MetricNames.NEXUS + MetricNames.DMP_WITH_GRANT, Gauge.build().name(MetricNames.NEXUS + MetricNames.DMP_WITH_GRANT).help("Number of Grants based on the status of the DMP that is using them during Nexus").labelNames("status").register(registry.getPrometheusRegistry())}, // {MetricNames.NEXUS + MetricNames.DMP_WITH_GRANT, Gauge.build().name(MetricNames.NEXUS + MetricNames.DMP_WITH_GRANT).help("Number of Grants based on the status of the DMP that is using them during Nexus").labelNames("status").register(registry.getPrometheusRegistry())},
//
{MetricNames.INSTALLATIONS, Gauge.build().name(MetricNames.INSTALLATIONS).help("Number of Installations").register(registry.getPrometheusRegistry())}, // {MetricNames.INSTALLATIONS, Gauge.build().name(MetricNames.INSTALLATIONS).help("Number of Installations").register(registry.getPrometheusRegistry())},
{MetricNames.NEXUS + MetricNames.INSTALLATIONS, Gauge.build().name(MetricNames.NEXUS + MetricNames.INSTALLATIONS).help("Number of Installations").register(registry.getPrometheusRegistry())}, // {MetricNames.NEXUS + MetricNames.INSTALLATIONS, Gauge.build().name(MetricNames.NEXUS + MetricNames.INSTALLATIONS).help("Number of Installations").register(registry.getPrometheusRegistry())},
}).collect(Collectors.toMap(data -> (String)data[0], data -> (Gauge) data[1])); }).collect(Collectors.toMap(data -> (String)data[0], data -> (Gauge) data[1]));

View File

@ -32,6 +32,7 @@ spring:
optional:classpath:config/dashboard.yml[.yml], optional:classpath:config/dashboard-${spring.profiles.active}.yml[.yml], optional:file:../config/dashboard-${spring.profiles.active}.yml[.yml], optional:classpath:config/dashboard.yml[.yml], optional:classpath:config/dashboard-${spring.profiles.active}.yml[.yml], optional:file:../config/dashboard-${spring.profiles.active}.yml[.yml],
optional:classpath:config/transformer.yml[.yml], optional:classpath:config/transformer-${spring.profiles.active}.yml[.yml], optional:file:../config/transformer-${spring.profiles.active}.yml[.yml], optional:classpath:config/transformer.yml[.yml], optional:classpath:config/transformer-${spring.profiles.active}.yml[.yml], optional:file:../config/transformer-${spring.profiles.active}.yml[.yml],
optional:classpath:config/authorization.yml[.yml], optional:classpath:config/authorization-${spring.profiles.active}.yml[.yml], optional:file:../config/authorization-${spring.profiles.active}.yml[.yml], optional:classpath:config/authorization.yml[.yml], optional:classpath:config/authorization-${spring.profiles.active}.yml[.yml], optional:file:../config/authorization-${spring.profiles.active}.yml[.yml],
optional:classpath:config/metrics.yml[.yml], optional:classpath:config/metrics-${spring.profiles.active}.yml[.yml], optional:file:../config/metrics-${spring.profiles.active}.yml[.yml],
optional:classpath:config/lock.yml[.yml], optional:classpath:config/lock-${spring.profiles.active}.yml[.yml], optional:file:../config/lock-${spring.profiles.active}.yml[.yml] optional:classpath:config/lock.yml[.yml], optional:classpath:config/lock-${spring.profiles.active}.yml[.yml], optional:file:../config/lock-${spring.profiles.active}.yml[.yml]

View File

@ -0,0 +1,30 @@
metrics:
task:
enable: true
intervalSeconds: 600
management:
endpoint:
metrics:
enabled: true
prometheus:
enabled: true
health:
show-details: always
web:
base-path: /
exposure:
include: ["prometheus","health","metrics" ]
metrics:
enabled: true
export:
prometheus:
enabled: true
enable:
http: true
jvm: true
jdbc: true
tomcat: true
logback: true
hikaricp: true
cache: true

View File

@ -2,7 +2,7 @@ web:
security: security:
enabled: true enabled: true
authorized-endpoints: [ api ] authorized-endpoints: [ api ]
allowed-endpoints: [ api/public, api/dmp/public, api/description/public, /api/supportive-material/public, api/language/public, api/contact-support/public, api/dashboard/public ] allowed-endpoints: [ api/public, api/dmp/public, api/description/public, /api/supportive-material/public, api/language/public, api/contact-support/public, api/dashboard/public, prometheus, health, metrics ]
idp: idp:
api-key: api-key:
enabled: false enabled: false

View File

@ -21,20 +21,25 @@ export class StorageFileService {
private get apiBase(): string { return `${this.configurationService.server}storage-file`; } private get apiBase(): string { return `${this.configurationService.server}storage-file`; }
getName(id: Guid ): Observable<string> { getSingle(id: Guid, reqFields: string[] = []): Observable<StorageFile> {
const url = `${this.apiBase}/name/${id}`; const url = `${this.apiBase}/${id}`;
const options = { params: { f: reqFields } };
return this.http return this.http
.get<string>(url).pipe( .get<StorageFile>(url, options).pipe(
catchError((error: any) => throwError(error))); catchError((error: any) => throwError(error)));
} }
download(id: Guid ): Observable<HttpResponse<Blob>> { download(id: Guid ): Observable<HttpResponse<Blob>> {
const url = `${this.apiBase}/${id}`; const url = `${this.apiBase}/download/${id}`;
const params = new BaseHttpParams();
params.interceptorContext = {
excludedInterceptors: [InterceptorType.JSONContentType, InterceptorType.ResponsePayload]
};
return this.http return this.http
.get<HttpResponse<Blob>>(url).pipe( .get(url, {params: params, responeType: 'blob', observe: 'response'});
catchError((error: any) => throwError(error)));
} }
uploadTempFiles(item: File): Observable<StorageFile[]> { uploadTempFiles(item: File): Observable<StorageFile[]> {

View File

@ -178,8 +178,8 @@
<app-description-template-editor-external-datasets-field-component *ngSwitchCase="descriptionTemplateFieldTypeEnum.EXTERNAL_DATASETS" class="col-12" [form]="form"></app-description-template-editor-external-datasets-field-component> <app-description-template-editor-external-datasets-field-component *ngSwitchCase="descriptionTemplateFieldTypeEnum.EXTERNAL_DATASETS" class="col-12" [form]="form"></app-description-template-editor-external-datasets-field-component>
<app-description-template-editor-multiplicity-field-component *ngSwitchCase="descriptionTemplateFieldTypeEnum.INTERNAL_ENTRIES_DMPS" class="col-12" [form]="form"></app-description-template-editor-multiplicity-field-component> <app-description-template-editor-label-and-multiplicity-field-component *ngSwitchCase="descriptionTemplateFieldTypeEnum.INTERNAL_ENTRIES_DMPS" class="col-12" [form]="form"></app-description-template-editor-label-and-multiplicity-field-component>
<app-description-template-editor-multiplicity-field-component *ngSwitchCase="descriptionTemplateFieldTypeEnum.INTERNAL_ENTRIES_DESCRIPTIONS" class="col-12" [form]="form"></app-description-template-editor-multiplicity-field-component> <app-description-template-editor-label-and-multiplicity-field-component *ngSwitchCase="descriptionTemplateFieldTypeEnum.INTERNAL_ENTRIES_DESCRIPTIONS" class="col-12" [form]="form"></app-description-template-editor-label-and-multiplicity-field-component>
</div> </div>
</ng-container> </ng-container>

View File

@ -47,13 +47,13 @@
<div class="row"> <div class="row">
<mat-form-field class="col-md-12"> <mat-form-field class="col-md-12">
<ng-container *ngIf="field.data.multipleSelect"> <ng-container *ngIf="field.data.multipleSelect">
<app-multiple-auto-complete placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [formControl]="propertiesFormGroup?.get(field.id).get('textListValue')" [configuration]="multipleAutoCompleteConfiguration"> <app-multiple-auto-complete placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [formControl]="propertiesFormGroup?.get(field.id).get('textListValue')" [configuration]="descriptionService.multipleAutocompleteConfiguration">
</app-multiple-auto-complete> </app-multiple-auto-complete>
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textListValue').getError('backendError').message}}</mat-error> <mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textListValue').getError('backendError').message}}</mat-error>
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</ng-container> </ng-container>
<ng-container *ngIf="!(field.data.multipleSelect)"> <ng-container *ngIf="!(field.data.multipleSelect)">
<app-single-auto-complete placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" [configuration]="singleAutoCompleteConfiguration" [required]="isRequired"> <app-single-auto-complete placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" [configuration]="descriptionService.singleAutocompleteConfiguration" [required]="isRequired">
</app-single-auto-complete> </app-single-auto-complete>
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textValue').getError('backendError').message}}</mat-error> <mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textValue').getError('backendError').message}}</mat-error>
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
@ -62,17 +62,17 @@
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
<div *ngSwitchCase="descriptionTemplateFieldTypeEnum.INTERNAL_DMP_ENTRIES_DMPS" class="col-12"> <div *ngSwitchCase="descriptionTemplateFieldTypeEnum.INTERNAL_ENTRIES_DMPS" class="col-12">
<div class="row"> <div class="row">
<mat-form-field class="col-md-12"> <mat-form-field class="col-md-12">
<ng-container *ngIf="field.data.multipleSelect"> <ng-container *ngIf="field.data.multipleSelect">
<app-multiple-auto-complete placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [formControl]="propertiesFormGroup?.get(field.id).get('textListValue')" [configuration]="multipleAutoCompleteConfiguration"> <app-multiple-auto-complete placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [formControl]="propertiesFormGroup?.get(field.id).get('textListValue')" [configuration]="dmpService.multipleAutocompleteConfiguration">
</app-multiple-auto-complete> </app-multiple-auto-complete>
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textListValue').getError('backendError').message}}</mat-error> <mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textListValue').getError('backendError').message}}</mat-error>
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</ng-container> </ng-container>
<ng-container *ngIf="!(field.data.multipleSelect)"> <ng-container *ngIf="!(field.data.multipleSelect)">
<app-single-auto-complete placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" [configuration]="singleAutoCompleteConfiguration" [required]="isRequired"> <app-single-auto-complete placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}" [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" [configuration]="dmpService.singleAutocompleteConfiguration" [required]="isRequired">
</app-single-auto-complete> </app-single-auto-complete>
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textValue').getError('backendError').message}}</mat-error> <mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textValue').getError('backendError').message}}</mat-error>
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
@ -107,9 +107,9 @@
</ng-container> </ng-container>
<ng-container *ngSwitchCase="descriptionTemplateFieldTypeEnum.UPLOAD"> <ng-container *ngSwitchCase="descriptionTemplateFieldTypeEnum.UPLOAD">
<div class="col-12 d-flex justify-content-center"> <div class="col-12 d-flex justify-content-center">
<ngx-dropzone #drop class="drop-file col-12" (change)="fileChangeEvent($event, true)" [multiple]="false" [accept]="typesToString()" [disabled]="propertiesFormGroup?.get(field.id).get('textValue').disabled"> <ngx-dropzone class="drop-file col-12" (change)="fileChangeEvent($event, true)" [multiple]="false" [accept]="typesToString()" [disabled]="propertiesFormGroup?.get(field.id).get('textValue').disabled">
<ngx-dropzone-preview *ngIf="propertiesFormGroup?.get(field.id).get('textValue').value && fileNameDisplay" class="file-preview" [removable]="true" (removed)="onRemove()"> <ngx-dropzone-preview *ngIf="propertiesFormGroup?.get(field.id).get('textValue').value" class="file-preview" [removable]="true" (removed)="onRemove()">
<ngx-dropzone-label class="file-label">{{ fileNameDisplay}}</ngx-dropzone-label> <ngx-dropzone-label class="file-label">{{ fileNameDisplay }}</ngx-dropzone-label>
</ngx-dropzone-preview> </ngx-dropzone-preview>
</ngx-dropzone> </ngx-dropzone>
</div> </div>
@ -157,7 +157,7 @@
<div *ngSwitchCase="descriptionTemplateFieldTypeEnum.TAGS" class="col-12"> <div *ngSwitchCase="descriptionTemplateFieldTypeEnum.TAGS" class="col-12">
<div class="row"> <div class="row">
<mat-form-field class="col-md-12"> <mat-form-field class="col-md-12">
<app-multiple-auto-complete [configuration]="tagsAutoCompleteConfiguration" [formControl]="propertiesFormGroup?.get(field.id).get('textListValue')" placeholder="{{('DATASET-EDITOR.FIELDS.TAGS' | translate) + (isRequired? ' *': '')}}"></app-multiple-auto-complete> <app-multiple-auto-complete [configuration]="tagService.multipleAutocompleteConfiguration" [formControl]="propertiesFormGroup?.get(field.id).get('textListValue')" placeholder="{{ (field.data.label | translate) + (isRequired? ' *': '') }}"></app-multiple-auto-complete>
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textListValue').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>

View File

@ -6,10 +6,13 @@ import { MatDialog } from "@angular/material/dialog";
import { DescriptionTemplateFieldType } from '@app/core/common/enum/description-template-field-type'; import { DescriptionTemplateFieldType } from '@app/core/common/enum/description-template-field-type';
import { DescriptionTemplateFieldValidationType } from '@app/core/common/enum/description-template-field-validation-type'; import { DescriptionTemplateFieldValidationType } from '@app/core/common/enum/description-template-field-validation-type';
import { DescriptionTemplateField, DescriptionTemplateFieldSet, DescriptionTemplateLabelAndMultiplicityData, DescriptionTemplateUploadData } from '@app/core/model/description-template/description-template'; import { DescriptionTemplateField, DescriptionTemplateFieldSet, DescriptionTemplateLabelAndMultiplicityData, DescriptionTemplateUploadData } from '@app/core/model/description-template/description-template';
import { StorageFile } from '@app/core/model/storage-file/storage-file';
import { DescriptionService } from '@app/core/services/description/description.service';
import { DmpService } from '@app/core/services/dmp/dmp.service'; import { DmpService } from '@app/core/services/dmp/dmp.service';
import { SnackBarNotificationLevel, UiNotificationService } from "@app/core/services/notification/ui-notification-service"; import { SnackBarNotificationLevel, UiNotificationService } from "@app/core/services/notification/ui-notification-service";
import { ReferenceService } from '@app/core/services/reference/reference.service'; import { ReferenceService } from '@app/core/services/reference/reference.service';
import { StorageFileService } from '@app/core/services/storage-file/storage-file.service'; import { StorageFileService } from '@app/core/services/storage-file/storage-file.service';
import { TagService } from '@app/core/services/tag/tag.service';
import { FileUtils } from '@app/core/services/utilities/file-utils.service'; import { FileUtils } from '@app/core/services/utilities/file-utils.service';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
@ -21,6 +24,7 @@ import { TranslateService } from '@ngx-translate/core';
import * as FileSaver from 'file-saver'; import * as FileSaver from 'file-saver';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators'; import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
@Component({ @Component({
selector: 'app-description-form-field', selector: 'app-description-form-field',
@ -70,11 +74,13 @@ export class DescriptionFormFieldComponent extends BaseComponent implements OnIn
]; ];
filesToUpload: FileList; filesToUpload: FileList;
fileNameDisplay: string; fileNameDisplay: string = null;
constructor( constructor(
private language: TranslateService, private language: TranslateService,
private dmpService: DmpService, public dmpService: DmpService,
public descriptionService: DescriptionService,
public tagService: TagService,
private cdr: ChangeDetectorRef, private cdr: ChangeDetectorRef,
private uiNotificationService: UiNotificationService, private uiNotificationService: UiNotificationService,
public dialog: MatDialog, public dialog: MatDialog,
@ -92,8 +98,24 @@ export class DescriptionFormFieldComponent extends BaseComponent implements OnIn
ngOnInit() { ngOnInit() {
if(this.field?.data?.fieldType == DescriptionTemplateFieldType.UPLOAD && this.field && this.field.id && (this.propertiesFormGroup?.get(this.field.id).get('textValue').value != undefined)) this.getUploadFileName(); if(this.field?.data?.fieldType == DescriptionTemplateFieldType.UPLOAD && this.field && this.field.id && (this.propertiesFormGroup?.get(this.field.id).get('textValue').value != undefined) && !this.fileNameDisplay) {
const id = Guid.parse((this.propertiesFormGroup?.get(this.field.id).get('textValue').value as string));
const fields = [
nameof<StorageFile>(x => x.id),
nameof<StorageFile>(x => x.name),
]
this.storageFileService.getSingle(id, fields).pipe(takeUntil(this._destroyed)).subscribe(storageFile => {
this.fileNameDisplay = storageFile.name;
this.applyFieldType();
});
} else {
this.applyFieldType();
}
}
private applyFieldType(){
this.isRequired = this.field.validations?.includes(DescriptionTemplateFieldValidationType.Required); this.isRequired = this.field.validations?.includes(DescriptionTemplateFieldValidationType.Required);
switch (this.field?.data?.fieldType) { switch (this.field?.data?.fieldType) {
@ -140,12 +162,6 @@ export class DescriptionFormFieldComponent extends BaseComponent implements OnIn
// this.form.disable(); // this.form.disable();
// } // }
break; break;
case DescriptionTemplateFieldType.INTERNAL_ENTRIES_DESCRIPTIONS:
this.makeAutocompleteConfiguration(this.searchDatasets.bind(this), "label");
break;
case DescriptionTemplateFieldType.INTERNAL_ENTRIES_DMPS:
this.makeAutocompleteConfiguration(this.searchDmps.bind(this), "label");
break;
} }
// this.form = this.visibilityRulesService.getFormGroup(this.field.id); // this.form = this.visibilityRulesService.getFormGroup(this.field.id);
@ -161,28 +177,6 @@ export class DescriptionFormFieldComponent extends BaseComponent implements OnIn
}); });
} }
searchDatasets(query: string) {
//TODO refactor
return null;
// let fields: Array<string> = new Array();
// const datasetsAutocompleteRequestItem: DataTableRequest<DatasetCriteria> = new DataTableRequest(0, 25, { fields: fields });
// datasetsAutocompleteRequestItem.criteria = new DatasetCriteria();
// datasetsAutocompleteRequestItem.criteria.like = query;
// //TODO: refactor this
// // return this.datasetService.getPaged(datasetsAutocompleteRequestItem).pipe(map(item => item.data));
// return null;
}
searchDmps(query: string) {
//TODO refactor
return null;
// let fields: Array<string> = new Array();
// const dmpsAutocompleteRequestItem: DataTableRequest<DmpCriteria> = new DataTableRequest(0, 25, { fields: fields });
// dmpsAutocompleteRequestItem.criteria = new DmpCriteria();
// dmpsAutocompleteRequestItem.criteria.like = query;
// return this.dmpService.getPaged(dmpsAutocompleteRequestItem).pipe(map(item => item.data));
}
makeAutocompleteConfiguration(myfunc: Function, title: string, subtitle?: string): void { makeAutocompleteConfiguration(myfunc: Function, title: string, subtitle?: string): void {
if (!((this.field.data as DescriptionTemplateLabelAndMultiplicityData).multipleSelect)) { if (!((this.field.data as DescriptionTemplateLabelAndMultiplicityData).multipleSelect)) {
this.singleAutoCompleteConfiguration = { this.singleAutoCompleteConfiguration = {
@ -275,16 +269,6 @@ export class DescriptionFormFieldComponent extends BaseComponent implements OnIn
} }
private getUploadFileName(){
const id = Guid.parse((this.propertiesFormGroup?.get(this.field.id).get('textValue').value as string));
this.storageFileService.getName(id)
.pipe(takeUntil(this._destroyed)).subscribe((name) => {
this.fileNameDisplay = name;
}, error => {
this.fileNameDisplay = null;
})
}
public upload() { public upload() {
this.storageFileService.uploadTempFiles(this.filesToUpload[0]) this.storageFileService.uploadTempFiles(this.filesToUpload[0])
@ -364,13 +348,12 @@ export class DescriptionFormFieldComponent extends BaseComponent implements OnIn
if (this.propertiesFormGroup?.get(this.field.id).get('textValue').value) { if (this.propertiesFormGroup?.get(this.field.id).get('textValue').value) {
const id = Guid.parse((this.propertiesFormGroup?.get(this.field.id).get('textValue').value as string)); const id = Guid.parse((this.propertiesFormGroup?.get(this.field.id).get('textValue').value as string));
this.storageFileService.download(id).subscribe((response) => { this.storageFileService.download(id).pipe(takeUntil(this._destroyed))
const blob = new Blob([response.body]); .subscribe(response => {
const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); const blob = new Blob([response.body]);
FileSaver.saveAs(blob, filename); const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition'));
}, error => { FileSaver.saveAs(blob, filename);
this.onCallbackUploadFail(error.error); });
})
} }
} }