diff --git a/module-core/src/main/java/io/goobi/vocabulary/api/MaintenanceController.java b/module-core/src/main/java/io/goobi/vocabulary/api/MaintenanceController.java new file mode 100644 index 0000000..f1303a6 --- /dev/null +++ b/module-core/src/main/java/io/goobi/vocabulary/api/MaintenanceController.java @@ -0,0 +1,29 @@ +package io.goobi.vocabulary.api; + +import io.goobi.vocabulary.service.manager.MaintenanceManager; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/maintenance") +public class MaintenanceController { + private final MaintenanceManager maintenanceManager; + + public MaintenanceController(MaintenanceManager maintenanceManager) { + this.maintenanceManager = maintenanceManager; + } + + @GetMapping("/selfcheck") + public ResponseEntity selfCheck() { + String status = maintenanceManager.testAllData(); + if (status.contains("FAIL")) { + return ResponseEntity.internalServerError() + .body(status); + } else { + return ResponseEntity.ok() + .body(status); + } + } +} diff --git a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldDefinitionEntity.java b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldDefinitionEntity.java index 1504bdb..462c0de 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldDefinitionEntity.java +++ b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldDefinitionEntity.java @@ -26,7 +26,7 @@ }) @Getter @Setter -public class FieldDefinitionEntity { +public class FieldDefinitionEntity implements Identifiable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", nullable = false) diff --git a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldInstanceEntity.java b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldInstanceEntity.java index 0030e33..f4ce67b 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldInstanceEntity.java +++ b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldInstanceEntity.java @@ -21,7 +21,7 @@ @Table(name = "field_instance") @Getter @Setter -public class FieldInstanceEntity { +public class FieldInstanceEntity implements Identifiable { private static final int MAX_LANGUAGE_LENGTH = 3; @Id diff --git a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldTranslationEntity.java b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldTranslationEntity.java index 192ac28..4b6fb7a 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldTranslationEntity.java +++ b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldTranslationEntity.java @@ -17,7 +17,7 @@ @Table(name = "field_translation") @Getter @Setter -public class FieldTranslationEntity { +public class FieldTranslationEntity implements Identifiable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", nullable = false) diff --git a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldTypeEntity.java b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldTypeEntity.java index 5b90f7b..ffffbeb 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldTypeEntity.java +++ b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldTypeEntity.java @@ -18,7 +18,7 @@ @Table(name = "field_type") @Getter @Setter -public class FieldTypeEntity { +public class FieldTypeEntity implements Identifiable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", nullable = false) diff --git a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldValueEntity.java b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldValueEntity.java index e1956d5..c89061b 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldValueEntity.java +++ b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/FieldValueEntity.java @@ -21,7 +21,7 @@ @Table(name = "field_value") @Getter @Setter -public class FieldValueEntity { +public class FieldValueEntity implements Identifiable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", nullable = false) diff --git a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/Identifiable.java b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/Identifiable.java new file mode 100644 index 0000000..7abc3b6 --- /dev/null +++ b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/Identifiable.java @@ -0,0 +1,5 @@ +package io.goobi.vocabulary.model.jpa; + +public interface Identifiable { + long getId(); +} diff --git a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/LanguageEntity.java b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/LanguageEntity.java index 35bc1fa..60625ea 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/LanguageEntity.java +++ b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/LanguageEntity.java @@ -14,7 +14,7 @@ @Table(name = "language") @Getter @Setter -public class LanguageEntity { +public class LanguageEntity implements Identifiable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", nullable = false) diff --git a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/SelectableValueEntity.java b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/SelectableValueEntity.java index 298c421..400f51c 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/SelectableValueEntity.java +++ b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/SelectableValueEntity.java @@ -16,7 +16,7 @@ @Table(name = "selectable_value") @Getter @Setter -public class SelectableValueEntity { +public class SelectableValueEntity implements Identifiable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", nullable = false) diff --git a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/TranslationDefinitionEntity.java b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/TranslationDefinitionEntity.java index 1f53642..1988ba5 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/TranslationDefinitionEntity.java +++ b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/TranslationDefinitionEntity.java @@ -20,7 +20,7 @@ }) @Getter @Setter -public class TranslationDefinitionEntity { +public class TranslationDefinitionEntity implements Identifiable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", nullable = false) diff --git a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/VocabularyEntity.java b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/VocabularyEntity.java index 1086c89..d1187eb 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/VocabularyEntity.java +++ b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/VocabularyEntity.java @@ -20,7 +20,7 @@ @Table(name = "vocabulary") @Getter @Setter -public class VocabularyEntity { +public class VocabularyEntity implements Identifiable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", nullable = false) diff --git a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/VocabularyRecordEntity.java b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/VocabularyRecordEntity.java index 8966639..c9cb76c 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/VocabularyRecordEntity.java +++ b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/VocabularyRecordEntity.java @@ -23,7 +23,7 @@ @Getter @Setter // Naming this class `Record` led to wrong behavior because of the introduction of Java records and some Spring Boot JPA logic -public class VocabularyRecordEntity { +public class VocabularyRecordEntity implements Identifiable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", nullable = false) diff --git a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/VocabularySchemaEntity.java b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/VocabularySchemaEntity.java index d86dbae..cb780f6 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/model/jpa/VocabularySchemaEntity.java +++ b/module-core/src/main/java/io/goobi/vocabulary/model/jpa/VocabularySchemaEntity.java @@ -18,7 +18,7 @@ @Table(name = "vocabulary_schema") @Getter @Setter -public class VocabularySchemaEntity { +public class VocabularySchemaEntity implements Identifiable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", nullable = false) diff --git a/module-core/src/main/java/io/goobi/vocabulary/repositories/FieldTypeRepository.java b/module-core/src/main/java/io/goobi/vocabulary/repositories/FieldTypeRepository.java index bec71a5..f68b5bd 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/repositories/FieldTypeRepository.java +++ b/module-core/src/main/java/io/goobi/vocabulary/repositories/FieldTypeRepository.java @@ -3,12 +3,12 @@ import io.goobi.vocabulary.model.jpa.FieldTypeEntity; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.ListCrudRepository; import org.springframework.lang.NonNull; import java.util.Optional; -public interface FieldTypeRepository extends CrudRepository { +public interface FieldTypeRepository extends ListCrudRepository { Page findAll(Pageable pageable); Optional findByName(@NonNull String name); diff --git a/module-core/src/main/java/io/goobi/vocabulary/repositories/LanguageRepository.java b/module-core/src/main/java/io/goobi/vocabulary/repositories/LanguageRepository.java index fa04f21..f95345b 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/repositories/LanguageRepository.java +++ b/module-core/src/main/java/io/goobi/vocabulary/repositories/LanguageRepository.java @@ -3,12 +3,12 @@ import io.goobi.vocabulary.model.jpa.LanguageEntity; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.ListCrudRepository; import org.springframework.lang.NonNull; import java.util.Optional; -public interface LanguageRepository extends CrudRepository { +public interface LanguageRepository extends ListCrudRepository { Optional findByAbbreviation(@NonNull String abbreviation); Page findAll(Pageable pageable); diff --git a/module-core/src/main/java/io/goobi/vocabulary/repositories/VocabularyRecordRepository.java b/module-core/src/main/java/io/goobi/vocabulary/repositories/VocabularyRecordRepository.java index 08704d0..f3c1e3a 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/repositories/VocabularyRecordRepository.java +++ b/module-core/src/main/java/io/goobi/vocabulary/repositories/VocabularyRecordRepository.java @@ -4,13 +4,13 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.ListCrudRepository; import org.springframework.lang.NonNull; import java.util.List; import java.util.Optional; -public interface VocabularyRecordRepository extends CrudRepository { +public interface VocabularyRecordRepository extends ListCrudRepository { @Override Optional findById(Long aLong); diff --git a/module-core/src/main/java/io/goobi/vocabulary/service/exchange/DTOMapperImpl.java b/module-core/src/main/java/io/goobi/vocabulary/service/exchange/DTOMapperImpl.java index f1d9c33..1273e03 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/service/exchange/DTOMapperImpl.java +++ b/module-core/src/main/java/io/goobi/vocabulary/service/exchange/DTOMapperImpl.java @@ -1,6 +1,5 @@ package io.goobi.vocabulary.service.exchange; -import io.goobi.vocabulary.api.assemblers.RecordAssembler; import io.goobi.vocabulary.exception.EntityNotFoundException; import io.goobi.vocabulary.exception.MissingAttributeException; import io.goobi.vocabulary.exchange.FieldDefinition; diff --git a/module-core/src/main/java/io/goobi/vocabulary/service/manager/MaintenanceManager.java b/module-core/src/main/java/io/goobi/vocabulary/service/manager/MaintenanceManager.java new file mode 100644 index 0000000..abfc4e5 --- /dev/null +++ b/module-core/src/main/java/io/goobi/vocabulary/service/manager/MaintenanceManager.java @@ -0,0 +1,83 @@ +package io.goobi.vocabulary.service.manager; + +import io.goobi.vocabulary.exception.VocabularyException; +import io.goobi.vocabulary.model.jpa.FieldTypeEntity; +import io.goobi.vocabulary.model.jpa.VocabularyEntity; +import io.goobi.vocabulary.model.jpa.VocabularyRecordEntity; +import io.goobi.vocabulary.model.jpa.VocabularySchemaEntity; +import io.goobi.vocabulary.repositories.FieldTypeRepository; +import io.goobi.vocabulary.repositories.VocabularyRecordRepository; +import io.goobi.vocabulary.repositories.VocabularyRepository; +import io.goobi.vocabulary.repositories.VocabularySchemaRepository; +import io.goobi.vocabulary.validation.Validator; +import org.springframework.data.repository.ListCrudRepository; +import org.springframework.stereotype.Service; + +import java.util.LinkedList; +import java.util.List; + +@Service +public class MaintenanceManager { + private final FieldTypeRepository fieldTypeRepository; + private final VocabularyRepository vocabularyRepository; + private final VocabularySchemaRepository vocabularySchemaRepository; + private final VocabularyRecordRepository vocabularyRecordRepository; + + private final Validator fieldTypeValidator; + private final Validator vocabularyValidator; + private final Validator vocabularySchemaValidator; + private final Validator vocabularyRecordValidator; + + public MaintenanceManager(FieldTypeRepository fieldTypeRepository, VocabularyRepository vocabularyRepository, VocabularySchemaRepository vocabularySchemaRepository, VocabularyRecordRepository vocabularyRecordRepository, Validator fieldTypeValidator, Validator vocabularyValidator, Validator vocabularySchemaValidator, Validator vocabularyRecordValidator) { + this.fieldTypeRepository = fieldTypeRepository; + this.vocabularyRepository = vocabularyRepository; + this.vocabularyValidator = vocabularyValidator; + this.vocabularySchemaRepository = vocabularySchemaRepository; + this.vocabularyRecordRepository = vocabularyRecordRepository; + this.fieldTypeValidator = fieldTypeValidator; + this.vocabularySchemaValidator = vocabularySchemaValidator; + this.vocabularyRecordValidator = vocabularyRecordValidator; + } + + public String testAllData() { + List selfCheckResults = new LinkedList<>(); + + selfCheckResults.add(selfTest("Types", fieldTypeRepository, fieldTypeValidator)); + selfCheckResults.add(selfTest("Vocabularies", vocabularyRepository, vocabularyValidator)); + selfCheckResults.add(selfTest("Vocabulary Schemas", vocabularySchemaRepository, vocabularySchemaValidator)); + selfCheckResults.add(selfTest("Vocabulary Records", vocabularyRecordRepository, vocabularyRecordValidator)); + + return String.join("\n", selfCheckResults); + } + + + private String selfTest(String entityName, ListCrudRepository repo, Validator validator) { + List errors = new LinkedList<>(); + for (Entity entity : repo.findAll()) { + try { + validator.validate(entity); + } catch (VocabularyException e) { + errors.add(dumpException(e, 1)); + } + } + if (errors.isEmpty()) { + return entityName + ": OK"; + } else { + return entityName + ": FAIL" + + String.join("", errors); + } + } + + private String dumpException(VocabularyException ex, int level) { + StringBuilder s = new StringBuilder(); + s.append('\n') + .append("\t".repeat(level)) + .append(ex.getMessage()); + if (ex.getCauses() != null) { + for (VocabularyException c : ex.getCauses()) { + s.append(dumpException(c, level + 1)); + } + } + return s.toString(); + } +} diff --git a/module-core/src/main/java/io/goobi/vocabulary/validation/BaseValidator.java b/module-core/src/main/java/io/goobi/vocabulary/validation/BaseValidator.java index b6c633e..a594763 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/validation/BaseValidator.java +++ b/module-core/src/main/java/io/goobi/vocabulary/validation/BaseValidator.java @@ -1,6 +1,7 @@ package io.goobi.vocabulary.validation; import io.goobi.vocabulary.exception.VocabularyException; +import io.goobi.vocabulary.model.jpa.Identifiable; import lombok.Setter; import java.util.LinkedList; @@ -8,7 +9,7 @@ import static io.goobi.vocabulary.exception.VocabularyException.ErrorCode.GenericValidation; -public class BaseValidator implements Validator { +public class BaseValidator implements Validator { private final String name; @Setter private List> validations; @@ -28,7 +29,7 @@ public final void validate(T t) throws VocabularyException { } } if (!errors.isEmpty()) { - throw new VocabularyException(GenericValidation, errors, null, (params) -> "Validation error"); + throw new VocabularyException(GenericValidation, errors, null, (params) -> "Validation error [" + t.getId() + "]"); } } } diff --git a/module-core/src/main/java/io/goobi/vocabulary/validation/RecordValidatorImpl.java b/module-core/src/main/java/io/goobi/vocabulary/validation/RecordValidatorImpl.java index 9f3be01..091c78b 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/validation/RecordValidatorImpl.java +++ b/module-core/src/main/java/io/goobi/vocabulary/validation/RecordValidatorImpl.java @@ -67,7 +67,7 @@ private void checkRequiredFieldsExistence(VocabularyRecordEntity vocabularyRecor .map(FieldDefinitionEntity::getName) .collect(Collectors.joining(",")) ), - params -> "Missing required fields: " + params.get("missingFields") + params -> "Missing required fields: " + params.get("missingFieldNames") ); } } diff --git a/module-core/src/main/java/io/goobi/vocabulary/validation/SchemaValidatorImpl.java b/module-core/src/main/java/io/goobi/vocabulary/validation/SchemaValidatorImpl.java index ae531bb..b1c19b1 100644 --- a/module-core/src/main/java/io/goobi/vocabulary/validation/SchemaValidatorImpl.java +++ b/module-core/src/main/java/io/goobi/vocabulary/validation/SchemaValidatorImpl.java @@ -14,7 +14,6 @@ import static io.goobi.vocabulary.exception.VocabularyException.ErrorCode.SchemaValidationMainFieldIsNotRequired; import static io.goobi.vocabulary.exception.VocabularyException.ErrorCode.SchemaValidationMissingMainField; import static io.goobi.vocabulary.exception.VocabularyException.ErrorCode.SchemaValidationNoDefinitions; -import static io.goobi.vocabulary.exception.VocabularyException.ErrorCode.SchemaValidationTitleFieldsAreNotRequired; import static io.goobi.vocabulary.exception.VocabularyException.ErrorCode.SchemaValidationTooManyMainFields; @Service