From 06372c97e9095575adff9f6faea4ff34b0d94bea Mon Sep 17 00:00:00 2001 From: Martin Ledvinka Date: Fri, 8 Nov 2024 17:16:06 +0100 Subject: [PATCH 1/9] [Doc] Update development documentation. --- README.md | 24 ++++++++--------- doc/implementation.md | 14 ++++------ doc/setup.md | 60 ++++++++++++------------------------------- 3 files changed, 34 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 7fe9747bf..079ac562b 100644 --- a/README.md +++ b/README.md @@ -31,29 +31,29 @@ See the [docs folder](doc/index.md) for additional information on implementation This section briefly lists the main technologies and principles used (or planned to be used) in the application. - Spring Boot 3, Spring Framework 6, Spring Security, Spring Data (paging, filtering) -- Jackson 2.13 +- Jackson Databind - [JB4JSON-LD](https://github.com/kbss-cvut/jb4jsonld-jackson) - Java - JSON-LD (de)serialization library - [JOPA](https://github.com/kbss-cvut/jopa) - persistence library for the Semantic Web -- JUnit 5 (RT used 4), Mockito 4 (RT used 1), Hamcrest 2 (RT used 1) -- Servlet API 4 (RT used 3.0.1) -- JSON Web Tokens (CSRF protection not necessary for JWT) +- JUnit 5, Mockito 4, Hamcrest 2 +- Jakarta Servlet API 4 +- JSON Web Tokens - SLF4J + Logback - CORS (for separate frontend) - Java bean validation (JSR 380) -## Ontology +## Ontologies -The ontology on which TermIt is based can be found in the `ontology` folder. For proper inference -functionality, `termit-model.ttl`, the -_popis-dat_ ontology model (http://onto.fel.cvut.cz/ontologies/slovnik/agendovy/popis-dat/model) and the SKOS vocabulary -model -(http://www.w3.org/TR/skos-reference/skos.rdf) need to be loaded into the repository used by TermIt (see `doc/setup.md`) -for details. +The ontology on which TermIt is based can be found in the `ontology` folder. It extends the +_popis-dat_ ontology (http://onto.fel.cvut.cz/ontologies/slovnik/agendovy/popis-dat). TermIt vocabularies and terms +use the SKOS vocabulary (http://www.w3.org/TR/skos-reference/skos.rdf). + +Relevant ontologies need to be loaded into the repository for proper inference functionality. See [setup.md](doc/setup.md) +for more details. ## Monitoring -We use [JavaMelody](https://github.com/javamelody/javamelody) for monitoring the application and its usage. The data are +[JavaMelody](https://github.com/javamelody/javamelody) can be used for monitoring the application and its usage. The data are available on the `/monitoring` endpoint and are secured using _basic_ authentication. Credentials are configured using the `javamelody.init-parameters.authorized-users` parameter in `application.yml` (see diff --git a/doc/implementation.md b/doc/implementation.md index 3d4005e5e..8518c25ec 100644 --- a/doc/implementation.md +++ b/doc/implementation.md @@ -43,23 +43,19 @@ follows: Fulltext search currently supports multiple types of implementation: * Simple substring matching on term and vocabulary label _(default)_ -* RDF4J with Lucene SAIL * GraphDB with Lucene connector Each implementation has its own search query which is loaded and used by `SearchDao`. In order for the more advanced -implementations for Lucene to work, a corresponding Maven profile (**graphdb**, **rdf4j**) has to be selected. This +implementation for Lucene to work, a corresponding Maven profile (**graphdb**) has to be selected. This inserts the correct query into the resulting artifact during build. If none of the profiles is selected, the default search is used. Note that in case of GraphDB, corresponding Lucene connectors (`label_index` for labels and `defcom_index` for -definitions and comments) -have to be created as well. +definitions and comments) have to be created as well. ### RDFS Inference in Tests -The test in-memory repository is configured to be a SPIN SAIL with RDFS inferencing engine. Thus, basically all the -inference features available in production are available in tests as well. However, the repository is by default left -empty (without the model or SPIN rules) to facilitate test performance (inference in RDF4J is really slow). To load the +The test in-memory repository is configured to be a RDF4J SAIL with RDFS inferencing engine. The repository is by default left +empty (without the model) to facilitate test performance (inference in RDF4J is really slow). To load the TermIt model into the repository and thus enable RDFS inference, call the `enableRdfsInference` -method available on both `BaseDaoTestRunner` and `BaseServiceTestRunner`. SPIN rules are currently not loaded as they -don't seem to be used by any tests. +method available on both `BaseDaoTestRunner` and `BaseServiceTestRunner`. diff --git a/doc/setup.md b/doc/setup.md index 81dad0f81..839fe0b3c 100644 --- a/doc/setup.md +++ b/doc/setup.md @@ -6,7 +6,7 @@ This guide provides information on how to build and deploy TermIt. ### System Requirements -* JDK 11 or newer (tested up to JDK 11 LTS) +* JDK 17 or newer * Apache Maven 3.5.x or newer @@ -16,13 +16,11 @@ This guide provides information on how to build and deploy TermIt. To build TermIt for **non**-development deployment, use Maven and select the `production` profile. -In addition, full text search in TermIt supports three modes: +In addition, full text search in TermIt supports two modes: 1. Default label-based substring matching -2. RDF4J repository with Lucene index -3. GraphDB repository with Lucene index +2. GraphDB repository with Lucene indexes -Options 2. and 3. have their respective Maven profiles - `rdf4j` and `graphdb`. Select one of them -or let the system use the default one. +Option 2. has its respective Maven profile - `graphdb`. Moreover, TermIt can be packaged either as an executable JAR (using Spring Boot) or as a WAR that can be deployed in any Servlet API 4-compatible application server. Maven profiles `standalone` (active by default) and `war` can be used to activate them respectively. @@ -40,9 +38,9 @@ There is one parameter not used by the application itself, but by Spring - `spri by the application: * `lucene` - decides whether Lucene text indexing is enabled and should be used in full text search queries. * `admin-registration-only` - decides whether new users can be registered only by application admin, or whether anyone can register. -* `no-cache` - disables EhCache which is used to cache lists of resources and vocabularies for faster retrieval. +* `no-cache` - disables Ehcache, which is used to cache lists of resources and vocabularies for faster retrieval, and persistence cache. -The `lucene` Spring profile is activated automatically by the `rdf4j` and `graphdb` Maven profiles. `admin-registration-only` and `no-cache` have to be added +The `lucene` Spring profile is activated automatically by the `graphdb` Maven. `admin-registration-only` and `no-cache` have to be added either in `application.yml` directly, or one can pass the parameter to Maven build, e.g.: * `mvn clean package -P graphdb "-Dspring.profiles.active=lucene,admin-registration-only"` @@ -51,7 +49,7 @@ either in `application.yml` directly, or one can pass the parameter to Maven bui #### Example * `mvn clean package -B -P production,graphdb "-Ddeployment=DEV"` -* `clean package -B -P production,rdf4j,war "-Ddeployment=STAGE"` +* `clean package -B -P production,graphdb,war "-Ddeployment=STAGE"` The `deployment` parameter is used to parameterize log messages and JMX beans and is important in case multiple deployments of TermIt are running in the same Tomcat. @@ -74,20 +72,17 @@ or configure it permanently by setting the `MAVEN_OPTS` variable in System Setti ### System Requirements -* JDK 11 or later (tested with JDK 11) -* (WAR) Apache Tomcat 8.5 or 9.x (recommended) or any Servlet API 4-compatible application server +* JDK 17 or later +* (WAR) Apache Tomcat 10 or any Jakarta Servlet API 4-compatible application server * _For deployment of a WAR build artifact._ - * Do not use Apache Tomcat 10.x, it is based on the new Jakarta EE and TermIt would not work on it due to package namespace issues (`javax` -> `jakarta`) + * Do not use Apache Tomcat 9.x or older, it is based on the old Java EE and TermIt would not work on it due to package namespace issues (`javax` -> `jakarta`) ### Setup Application deployment is simple - just deploy the WAR file (in case of the `war` Maven build profile) to an application server or run the JAR file (in case of the `standalone` Maven build profile). -What is important is the correct setup of the repository. We will describe two options: - -1. GraphDB -2. RDF4J +What is important is the correct setup of the repository. #### GraphDB @@ -99,16 +94,16 @@ In order to support inference used by the application, a custom ruleset has to b 4. Create the following Lucene connectors in GraphDB: * *Label index* * name: **label_index** - * Field name: **label**, **title** - * Property chain: **http://www.w3.org/2000/01/rdf-schema#label**, **http://purl.org/dc/terms/title** + * Field names: **prefLabel**, **altLabel**, **hiddenLabel**, **title** + * Property chains: **http://www.w3.org/2004/02/skos/core#prefLabel**, http://www.w3.org/2004/02/skos/core#altLabel**, **http://www.w3.org/2004/02/skos/core#hiddenLabel**, **http://purl.org/dc/terms/title** * Languages: _Leave empty (for indexing all languages) or specify the language tag - see below_ * Types: **http://www.w3.org/2004/02/skos/core#Concept**, **http://onto.fel.cvut.cz/ontologies/slovník/agendový/popis-dat/pojem/slovník** * Analyzer: Analyzer appropriate for the system language, e.g. **org.apache.lucene.analysis.cz.CzechAnalyzer** * *Definition and comment index* * name: **defcom_index** - * Field name: **definition**, **comment**, **description** + * Field name: **definition**, **scopeNote**, **description** * Languages: _Leave empty (for indexing all languages) or specify the language tag - see below_ - * Property chain: **http://www.w3.org/2004/02/skos/core#definition**, **http://www.w3.org/2000/01/rdf-schema#comment**, **http://purl.org/dc/terms/description** + * Property chain: **http://www.w3.org/2004/02/skos/core#definition**, **http://www.w3.org/2004/02/skos/core#scopeNote**, **http://purl.org/dc/terms/description** * Types and Analyzer as above Language can be set for each connector. This is useful in case the data contain labels, definitions, and comments in multiple languages. In this case, @@ -117,34 +112,13 @@ there is a term with label `území`@cs and `area`@en. Now, if no language is sp look as follows: `území area`, which may not be desired. If the connector language is set to `cs`, the result snippet will contain only `území`. See the [documentation](http://graphdb.ontotext.com/documentation/free/lucene-graphdb-connector.html) for more details. -#### RDF4J - -In order to support the inference used by the application, new rules need to be added to RDF4J because its own RDFS rule engine does not -support OWL stuff like inverse properties (which are used in the model). - -For RDF4J 2.x: -1. Start by creating an RDF4J repository of type **RDFS+SPIN with Lucene support** -2. Upload SPIN rules from `rulesets/rules-termit-spin.ttl` into the repository -3. There is no need to configure Lucene connectors, it by default indexes all properties in RDF4J (alternatively, it is possible -to upload a repository configuration directly into the system repository - see examples at [[1]](https://github.com/eclipse/rdf4j/tree/master/core/repository/api/src/main/resources/org/eclipse/rdf4j/repository/config) -4. ----- - -For RDF4J 3.x: -1. Start by creating an RDF4J repository with RDFS and SPIN inference and Lucene support - * Copy repository configuration into the appropriate directory, as described at [[2]](https://rdf4j.eclipse.org/documentation/server-workbench-console/#repository-configuration) - * Native store with RDFS+SPIN and Lucene sample configuration is at [[3]](https://github.com/eclipse/rdf4j/blob/master/core/repository/api/src/main/resources/org/eclipse/rdf4j/repository/config/native-spin-rdfs-lucene.ttl) -2. Upload SPIN rules from `rulesets/rules-termit-spin.ttl` into the repository -3. There is no need to configure Lucene connectors, it by default indexes all properties in RDF4J -4. ----- - -#### Common - TermIt needs the repository to provide some inference. Beside loading the appropriate rulesets (see above), it is also necessary to load the ontological models into the repository. 5. Upload the following RDF files into the newly created repository: * `ontology/termit-glosář.ttl` * `ontology/termit-model.ttl` + * `ontology/sioc-ns.rdf` * `http://onto.fel.cvut.cz/ontologies/slovník/agendový/popis-dat/model` * `http://onto.fel.cvut.cz/ontologies/slovník/agendový/popis-dat/glosář` * `https://www.w3.org/TR/skos-reference/skos.rdf` @@ -203,4 +177,4 @@ TERMIT_SECURITY_PROVIDER=oidc TermIt will automatically configure its security accordingly (it is using Spring's [`ConditionalOnProperty`](https://www.baeldung.com/spring-conditionalonproperty)). -**Note that termit-ui needs to be configured for mathcing authentication mode.** +**Note that termit-ui needs to be configured for matching authentication mode.** From 5352045e708143ec9fa19d6cc2dbd2e434f8882b Mon Sep 17 00:00:00 2001 From: Martin Ledvinka Date: Fri, 8 Nov 2024 17:16:25 +0100 Subject: [PATCH 2/9] [3.3.0] Bump version. --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1ac6959e8..6a644e94d 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ termit - 3.2.0 + 3.3.0 TermIt Terminology manager based on Semantic Web technologies. ${packaging} @@ -394,7 +394,7 @@ - + graphdb From 78765d031b81aeb414aa2fb9fda4f3f150f3a131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Ka=C5=88ka?= Date: Sat, 19 Oct 2024 15:42:02 +0200 Subject: [PATCH 3/9] [Enhancement kbss-cvut/termit-ui#530] Create DeleteChangeRecord class and BeforeAssetDeleteEvent Created classes required for recording an asset removal, implemented dispatching asset delete event and logic for ChangeTracker handling the event. --- .../termit/event/BeforeAssetDeleteEvent.java | 19 +++++ .../changetracking/DeleteChangeRecord.java | 84 +++++++++++++++++++ .../changetracking/PersistChangeRecord.java | 3 + .../changetracking/UpdateChangeRecord.java | 3 + .../termit/persistence/dao/BaseAssetDao.java | 8 ++ .../termit/persistence/dao/VocabularyDao.java | 2 + .../service/changetracking/ChangeTracker.java | 28 +++++++ .../resources/ontologies/popis-dat-model.ttl | 4 + 8 files changed, 151 insertions(+) create mode 100644 src/main/java/cz/cvut/kbss/termit/event/BeforeAssetDeleteEvent.java create mode 100644 src/main/java/cz/cvut/kbss/termit/model/changetracking/DeleteChangeRecord.java diff --git a/src/main/java/cz/cvut/kbss/termit/event/BeforeAssetDeleteEvent.java b/src/main/java/cz/cvut/kbss/termit/event/BeforeAssetDeleteEvent.java new file mode 100644 index 000000000..e8e6e363d --- /dev/null +++ b/src/main/java/cz/cvut/kbss/termit/event/BeforeAssetDeleteEvent.java @@ -0,0 +1,19 @@ +package cz.cvut.kbss.termit.event; + +import cz.cvut.kbss.termit.model.Asset; +import org.springframework.context.ApplicationEvent; + +/** + * Event published before an asset is deleted. + */ +public class BeforeAssetDeleteEvent> extends ApplicationEvent { + final T asset; + public BeforeAssetDeleteEvent(Object source, T asset) { + super(source); + this.asset = asset; + } + + public T getAsset() { + return asset; + } +} diff --git a/src/main/java/cz/cvut/kbss/termit/model/changetracking/DeleteChangeRecord.java b/src/main/java/cz/cvut/kbss/termit/model/changetracking/DeleteChangeRecord.java new file mode 100644 index 000000000..437df6f22 --- /dev/null +++ b/src/main/java/cz/cvut/kbss/termit/model/changetracking/DeleteChangeRecord.java @@ -0,0 +1,84 @@ +package cz.cvut.kbss.termit.model.changetracking; + +import cz.cvut.kbss.jopa.model.MultilingualString; +import cz.cvut.kbss.jopa.model.annotations.OWLAnnotationProperty; +import cz.cvut.kbss.jopa.model.annotations.OWLObjectProperty; +import cz.cvut.kbss.jopa.model.annotations.ParticipationConstraints; +import cz.cvut.kbss.jopa.vocabulary.DC; +import cz.cvut.kbss.termit.model.Asset; +import cz.cvut.kbss.termit.util.Vocabulary; +import jakarta.annotation.Nonnull; + +import java.io.Serializable; +import java.net.URI; +import java.util.Objects; + +/** + * Represents a record of asset deletion. + * @param The label type, {@link String} or {@link MultilingualString} + */ +//@OWLClass(iri = Vocabulary.s_c_smazani_entity) TODO: ontology for DeleteChangeRecord +public class DeleteChangeRecord extends AbstractChangeRecord { + @ParticipationConstraints(nonEmpty = true) + @OWLAnnotationProperty(iri = DC.Terms.TITLE) + private T label; + + @OWLObjectProperty(iri = Vocabulary.s_p_je_pojmem_ze_slovniku) + private URI vocabulary; + + public DeleteChangeRecord(Asset changedEntity, URI vocabulary) { + super(changedEntity); + this.label = changedEntity.getLabel(); + this.vocabulary = vocabulary; + } + + public T getLabel() { + return label; + } + + public void setLabel(T label) { + this.label = label; + } + + public URI getVocabulary() { + return vocabulary; + } + + public void setVocabulary(URI vocabulary) { + this.vocabulary = vocabulary; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof DeleteChangeRecord that)) { + return false; + } + if (!super.equals(o)) { + return false; + } + return Objects.equals(label, that.label) && Objects.equals(vocabulary, that.vocabulary); + } + + @Override + public String toString() { + return "DeleteChangeRecord{" + + super.toString() + + ", label=" + label + + (vocabulary != null ? ", vocabulary=" + vocabulary : "") + + '}'; + } + + @Override + public int compareTo(@Nonnull AbstractChangeRecord o) { + if (o instanceof UpdateChangeRecord) { + return 1; + } + if (o instanceof PersistChangeRecord) { + return 1; + } + return super.compareTo(o); + } +} diff --git a/src/main/java/cz/cvut/kbss/termit/model/changetracking/PersistChangeRecord.java b/src/main/java/cz/cvut/kbss/termit/model/changetracking/PersistChangeRecord.java index 6fccde3d6..ed1c675af 100644 --- a/src/main/java/cz/cvut/kbss/termit/model/changetracking/PersistChangeRecord.java +++ b/src/main/java/cz/cvut/kbss/termit/model/changetracking/PersistChangeRecord.java @@ -42,6 +42,9 @@ public int compareTo(@Nonnull AbstractChangeRecord o) { if (o instanceof UpdateChangeRecord) { return -1; } + if (o instanceof DeleteChangeRecord) { + return -1; + } return super.compareTo(o); } } diff --git a/src/main/java/cz/cvut/kbss/termit/model/changetracking/UpdateChangeRecord.java b/src/main/java/cz/cvut/kbss/termit/model/changetracking/UpdateChangeRecord.java index e1220f9f4..93074f63e 100644 --- a/src/main/java/cz/cvut/kbss/termit/model/changetracking/UpdateChangeRecord.java +++ b/src/main/java/cz/cvut/kbss/termit/model/changetracking/UpdateChangeRecord.java @@ -105,6 +105,9 @@ public int compareTo(@Nonnull AbstractChangeRecord o) { if (o instanceof PersistChangeRecord) { return 1; } + if (o instanceof DeleteChangeRecord) { + return -1; + } return super.compareTo(o); } } diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDao.java b/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDao.java index bb6e26400..297af3676 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDao.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDao.java @@ -21,6 +21,7 @@ import cz.cvut.kbss.termit.dto.RecentlyCommentedAsset; import cz.cvut.kbss.termit.event.AssetPersistEvent; import cz.cvut.kbss.termit.event.AssetUpdateEvent; +import cz.cvut.kbss.termit.event.BeforeAssetDeleteEvent; import cz.cvut.kbss.termit.exception.PersistenceException; import cz.cvut.kbss.termit.model.Asset; import cz.cvut.kbss.termit.model.User; @@ -40,6 +41,7 @@ * Base DAO implementation for assets managed by the application. * * @param Type of the asset + * @param Type of the asset's label */ public abstract class BaseAssetDao> extends BaseDao { @@ -65,6 +67,12 @@ public T update(T entity) { return super.update(entity); } + @Override + public void remove(T entity) { + eventPublisher.publishEvent(new BeforeAssetDeleteEvent(this, entity)); + super.remove(entity); + } + /** * Finds unique last commented assets. * diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDao.java b/src/main/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDao.java index d0cd42ea8..d0fbdc893 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDao.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDao.java @@ -30,6 +30,7 @@ import cz.cvut.kbss.termit.dto.Snapshot; import cz.cvut.kbss.termit.event.AssetPersistEvent; import cz.cvut.kbss.termit.event.AssetUpdateEvent; +import cz.cvut.kbss.termit.event.BeforeAssetDeleteEvent; import cz.cvut.kbss.termit.event.RefreshLastModifiedEvent; import cz.cvut.kbss.termit.event.VocabularyWillBeRemovedEvent; import cz.cvut.kbss.termit.exception.PersistenceException; @@ -228,6 +229,7 @@ public Vocabulary update(Vocabulary entity) { @Override public void remove(Vocabulary entity) { eventPublisher.publishEvent(new VocabularyWillBeRemovedEvent(this, entity.getUri())); + eventPublisher.publishEvent(new BeforeAssetDeleteEvent(this, entity)); this.removeVocabulary(entity, true); } diff --git a/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java b/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java index b9497ab94..8a5b70fac 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java +++ b/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java @@ -19,9 +19,12 @@ import cz.cvut.kbss.termit.event.AssetPersistEvent; import cz.cvut.kbss.termit.event.AssetUpdateEvent; +import cz.cvut.kbss.termit.event.BeforeAssetDeleteEvent; +import cz.cvut.kbss.termit.model.AbstractTerm; import cz.cvut.kbss.termit.model.Asset; import cz.cvut.kbss.termit.model.User; import cz.cvut.kbss.termit.model.changetracking.AbstractChangeRecord; +import cz.cvut.kbss.termit.model.changetracking.DeleteChangeRecord; import cz.cvut.kbss.termit.model.changetracking.PersistChangeRecord; import cz.cvut.kbss.termit.model.changetracking.UpdateChangeRecord; import cz.cvut.kbss.termit.model.resource.File; @@ -37,6 +40,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.Serializable; +import java.net.URI; import java.time.Instant; import java.util.Collection; import java.util.stream.Collectors; @@ -114,4 +119,27 @@ public void onAssetPersistEvent(@Nonnull AssetPersistEvent event) { changeRecord.setTimestamp(Utils.timestamp()); changeRecordDao.persist(changeRecord, added); } + + /** + * Records an asset deletion from the repository. + * + * @param event Event representing the asset deletion + */ + @Transactional + @EventListener + public > void onBeforeAssetDeleteEvent(@Nonnull BeforeAssetDeleteEvent event) { + final T asset = event.getAsset(); + LOG.trace("Recording deletion of asset {}.", asset); + + URI vocabulary = null; + if (asset instanceof AbstractTerm term) { + vocabulary = term.getVocabulary(); + } + + final AbstractChangeRecord changeRecord = new DeleteChangeRecord(asset, vocabulary); + changeRecord.setAuthor(securityUtils.getCurrentUser().toUser()); + changeRecord.setTimestamp(Utils.timestamp()); + + changeRecordDao.persist(changeRecord, asset); + } } diff --git a/src/test/resources/ontologies/popis-dat-model.ttl b/src/test/resources/ontologies/popis-dat-model.ttl index 180b9a5a1..04152b693 100644 --- a/src/test/resources/ontologies/popis-dat-model.ttl +++ b/src/test/resources/ontologies/popis-dat-model.ttl @@ -536,3 +536,7 @@ a-popis-dat-pojem:má-původní-hodnotu a ; rdfs:domain a-popis-dat-pojem:úprava-entity ; rdfs:subPropertyOf . + +a-popis-dat-pojem:smazání-entity + a , owl:Class ; + rdfs:subClassOf a-popis-dat-pojem:změna . From 82f6bb352c90ee0ab986476097154b7fb78d25e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Ka=C5=88ka?= Date: Sun, 27 Oct 2024 12:04:11 +0100 Subject: [PATCH 4/9] [Enhancement kbss-cvut/termit-ui#530] Remove generics for DeleteChangeRecord Using type capture instead. --- .../termit/event/BeforeAssetDeleteEvent.java | 8 ++-- .../changetracking/DeleteChangeRecord.java | 41 +++++++++++++------ .../termit/persistence/dao/BaseAssetDao.java | 3 +- .../termit/persistence/dao/VocabularyDao.java | 16 ++++---- .../service/changetracking/ChangeTracker.java | 7 ++-- 5 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/main/java/cz/cvut/kbss/termit/event/BeforeAssetDeleteEvent.java b/src/main/java/cz/cvut/kbss/termit/event/BeforeAssetDeleteEvent.java index e8e6e363d..ddbdee1e0 100644 --- a/src/main/java/cz/cvut/kbss/termit/event/BeforeAssetDeleteEvent.java +++ b/src/main/java/cz/cvut/kbss/termit/event/BeforeAssetDeleteEvent.java @@ -6,14 +6,14 @@ /** * Event published before an asset is deleted. */ -public class BeforeAssetDeleteEvent> extends ApplicationEvent { - final T asset; - public BeforeAssetDeleteEvent(Object source, T asset) { +public class BeforeAssetDeleteEvent extends ApplicationEvent { + final Asset asset; + public BeforeAssetDeleteEvent(Object source, Asset asset) { super(source); this.asset = asset; } - public T getAsset() { + public Asset getAsset() { return asset; } } diff --git a/src/main/java/cz/cvut/kbss/termit/model/changetracking/DeleteChangeRecord.java b/src/main/java/cz/cvut/kbss/termit/model/changetracking/DeleteChangeRecord.java index 437df6f22..27657e808 100644 --- a/src/main/java/cz/cvut/kbss/termit/model/changetracking/DeleteChangeRecord.java +++ b/src/main/java/cz/cvut/kbss/termit/model/changetracking/DeleteChangeRecord.java @@ -2,41 +2,58 @@ import cz.cvut.kbss.jopa.model.MultilingualString; import cz.cvut.kbss.jopa.model.annotations.OWLAnnotationProperty; +import cz.cvut.kbss.jopa.model.annotations.OWLClass; import cz.cvut.kbss.jopa.model.annotations.OWLObjectProperty; import cz.cvut.kbss.jopa.model.annotations.ParticipationConstraints; -import cz.cvut.kbss.jopa.vocabulary.DC; +import cz.cvut.kbss.jopa.vocabulary.RDFS; import cz.cvut.kbss.termit.model.Asset; import cz.cvut.kbss.termit.util.Vocabulary; import jakarta.annotation.Nonnull; -import java.io.Serializable; import java.net.URI; import java.util.Objects; /** * Represents a record of asset deletion. - * @param The label type, {@link String} or {@link MultilingualString} */ -//@OWLClass(iri = Vocabulary.s_c_smazani_entity) TODO: ontology for DeleteChangeRecord -public class DeleteChangeRecord extends AbstractChangeRecord { +@OWLClass(iri = Vocabulary.s_c_smazani_entity) +public class DeleteChangeRecord extends AbstractChangeRecord { @ParticipationConstraints(nonEmpty = true) - @OWLAnnotationProperty(iri = DC.Terms.TITLE) - private T label; + @OWLAnnotationProperty(iri = RDFS.LABEL) + private MultilingualString label; @OWLObjectProperty(iri = Vocabulary.s_p_je_pojmem_ze_slovniku) private URI vocabulary; - public DeleteChangeRecord(Asset changedEntity, URI vocabulary) { + /** + * Creates a new instance. + * @param changedEntity the changed asset + * @param vocabulary optional vocabulary URI + * @throws IllegalArgumentException If the label type is not String or MultilingualString + */ + public DeleteChangeRecord(Asset changedEntity, URI vocabulary) { super(changedEntity); - this.label = changedEntity.getLabel(); + + if (changedEntity.getLabel() instanceof String stringLabel) { + this.label = MultilingualString.create(stringLabel, null); + } else if (changedEntity.getLabel() instanceof MultilingualString multilingualLabel) { + this.label = multilingualLabel; + } else { + throw new IllegalArgumentException("Unsupported label type: " + changedEntity.getLabel().getClass()); + } + this.vocabulary = vocabulary; } - public T getLabel() { + public DeleteChangeRecord() { + super(); + } + + public MultilingualString getLabel() { return label; } - public void setLabel(T label) { + public void setLabel(MultilingualString label) { this.label = label; } @@ -53,7 +70,7 @@ public boolean equals(Object o) { if (this == o) { return true; } - if (!(o instanceof DeleteChangeRecord that)) { + if (!(o instanceof DeleteChangeRecord that)) { return false; } if (!super.equals(o)) { diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDao.java b/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDao.java index 297af3676..831961df5 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDao.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/dao/BaseAssetDao.java @@ -41,7 +41,6 @@ * Base DAO implementation for assets managed by the application. * * @param Type of the asset - * @param Type of the asset's label */ public abstract class BaseAssetDao> extends BaseDao { @@ -69,7 +68,7 @@ public T update(T entity) { @Override public void remove(T entity) { - eventPublisher.publishEvent(new BeforeAssetDeleteEvent(this, entity)); + eventPublisher.publishEvent(new BeforeAssetDeleteEvent(this, entity)); super.remove(entity); } diff --git a/src/main/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDao.java b/src/main/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDao.java index d0fbdc893..02230ea73 100644 --- a/src/main/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDao.java +++ b/src/main/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDao.java @@ -62,12 +62,12 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Stream; import static cz.cvut.kbss.termit.util.Constants.DEFAULT_PAGE_SIZE; import static cz.cvut.kbss.termit.util.Constants.SKOS_CONCEPT_MATCH_RELATIONSHIPS; @@ -229,7 +229,7 @@ public Vocabulary update(Vocabulary entity) { @Override public void remove(Vocabulary entity) { eventPublisher.publishEvent(new VocabularyWillBeRemovedEvent(this, entity.getUri())); - eventPublisher.publishEvent(new BeforeAssetDeleteEvent(this, entity)); + eventPublisher.publishEvent(new BeforeAssetDeleteEvent(this, entity)); this.removeVocabulary(entity, true); } @@ -386,11 +386,13 @@ public List getChangesOfContent(Vocabulary vocabulary) { .setParameter("type", URI.create( cz.cvut.kbss.termit.util.Vocabulary.s_c_uprava_entity)).getResultList(); updates.forEach(u -> u.addType(cz.cvut.kbss.termit.util.Vocabulary.s_c_uprava_entity)); - final List result = new ArrayList<>(persists.size() + updates.size()); - result.addAll(persists); - result.addAll(updates); - Collections.sort(result); - return result; + final List deletitions = createContentChangesQuery(vocabulary) + .setParameter("type", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_c_smazani_entity)).getResultList(); + deletitions.forEach(d -> d.addType(cz.cvut.kbss.termit.util.Vocabulary.s_c_smazani_entity)); + return Stream.of(persists, updates, deletitions) + .flatMap(List::stream) + .sorted() + .toList(); } /** diff --git a/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java b/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java index 8a5b70fac..fb9ab565d 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java +++ b/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java @@ -40,7 +40,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.io.Serializable; import java.net.URI; import java.time.Instant; import java.util.Collection; @@ -127,8 +126,8 @@ public void onAssetPersistEvent(@Nonnull AssetPersistEvent event) { */ @Transactional @EventListener - public > void onBeforeAssetDeleteEvent(@Nonnull BeforeAssetDeleteEvent event) { - final T asset = event.getAsset(); + public void onBeforeAssetDeleteEvent(@Nonnull BeforeAssetDeleteEvent event) { + final Asset asset = event.getAsset(); LOG.trace("Recording deletion of asset {}.", asset); URI vocabulary = null; @@ -136,7 +135,7 @@ public > void onBeforeAssetDeleteEven vocabulary = term.getVocabulary(); } - final AbstractChangeRecord changeRecord = new DeleteChangeRecord(asset, vocabulary); + final AbstractChangeRecord changeRecord = new DeleteChangeRecord(asset, vocabulary); changeRecord.setAuthor(securityUtils.getCurrentUser().toUser()); changeRecord.setTimestamp(Utils.timestamp()); From e331541dde00404d59e1a6c85e9a3e4ecdfd560f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Ka=C5=88ka?= Date: Sun, 27 Oct 2024 13:53:44 +0100 Subject: [PATCH 5/9] [Enhancement kbss-cvut/termit-ui#530] Remove vocabulary from DeleteChangeRecord The record is already saved in tha vocabulary context. --- .../changetracking/DeleteChangeRecord.java | 21 ++----------------- .../service/changetracking/ChangeTracker.java | 9 +------- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/src/main/java/cz/cvut/kbss/termit/model/changetracking/DeleteChangeRecord.java b/src/main/java/cz/cvut/kbss/termit/model/changetracking/DeleteChangeRecord.java index 27657e808..1d2cdc98c 100644 --- a/src/main/java/cz/cvut/kbss/termit/model/changetracking/DeleteChangeRecord.java +++ b/src/main/java/cz/cvut/kbss/termit/model/changetracking/DeleteChangeRecord.java @@ -3,14 +3,12 @@ import cz.cvut.kbss.jopa.model.MultilingualString; import cz.cvut.kbss.jopa.model.annotations.OWLAnnotationProperty; import cz.cvut.kbss.jopa.model.annotations.OWLClass; -import cz.cvut.kbss.jopa.model.annotations.OWLObjectProperty; import cz.cvut.kbss.jopa.model.annotations.ParticipationConstraints; import cz.cvut.kbss.jopa.vocabulary.RDFS; import cz.cvut.kbss.termit.model.Asset; import cz.cvut.kbss.termit.util.Vocabulary; import jakarta.annotation.Nonnull; -import java.net.URI; import java.util.Objects; /** @@ -22,16 +20,12 @@ public class DeleteChangeRecord extends AbstractChangeRecord { @OWLAnnotationProperty(iri = RDFS.LABEL) private MultilingualString label; - @OWLObjectProperty(iri = Vocabulary.s_p_je_pojmem_ze_slovniku) - private URI vocabulary; - /** * Creates a new instance. * @param changedEntity the changed asset - * @param vocabulary optional vocabulary URI * @throws IllegalArgumentException If the label type is not String or MultilingualString */ - public DeleteChangeRecord(Asset changedEntity, URI vocabulary) { + public DeleteChangeRecord(Asset changedEntity) { super(changedEntity); if (changedEntity.getLabel() instanceof String stringLabel) { @@ -41,8 +35,6 @@ public DeleteChangeRecord(Asset changedEntity, URI vocabulary) { } else { throw new IllegalArgumentException("Unsupported label type: " + changedEntity.getLabel().getClass()); } - - this.vocabulary = vocabulary; } public DeleteChangeRecord() { @@ -57,14 +49,6 @@ public void setLabel(MultilingualString label) { this.label = label; } - public URI getVocabulary() { - return vocabulary; - } - - public void setVocabulary(URI vocabulary) { - this.vocabulary = vocabulary; - } - @Override public boolean equals(Object o) { if (this == o) { @@ -76,7 +60,7 @@ public boolean equals(Object o) { if (!super.equals(o)) { return false; } - return Objects.equals(label, that.label) && Objects.equals(vocabulary, that.vocabulary); + return Objects.equals(label, that.label); } @Override @@ -84,7 +68,6 @@ public String toString() { return "DeleteChangeRecord{" + super.toString() + ", label=" + label + - (vocabulary != null ? ", vocabulary=" + vocabulary : "") + '}'; } diff --git a/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java b/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java index fb9ab565d..a7a5876b7 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java +++ b/src/main/java/cz/cvut/kbss/termit/service/changetracking/ChangeTracker.java @@ -20,7 +20,6 @@ import cz.cvut.kbss.termit.event.AssetPersistEvent; import cz.cvut.kbss.termit.event.AssetUpdateEvent; import cz.cvut.kbss.termit.event.BeforeAssetDeleteEvent; -import cz.cvut.kbss.termit.model.AbstractTerm; import cz.cvut.kbss.termit.model.Asset; import cz.cvut.kbss.termit.model.User; import cz.cvut.kbss.termit.model.changetracking.AbstractChangeRecord; @@ -40,7 +39,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.net.URI; import java.time.Instant; import java.util.Collection; import java.util.stream.Collectors; @@ -130,12 +128,7 @@ public void onBeforeAssetDeleteEvent(@Nonnull BeforeAssetDeleteEvent event) { final Asset asset = event.getAsset(); LOG.trace("Recording deletion of asset {}.", asset); - URI vocabulary = null; - if (asset instanceof AbstractTerm term) { - vocabulary = term.getVocabulary(); - } - - final AbstractChangeRecord changeRecord = new DeleteChangeRecord(asset, vocabulary); + final AbstractChangeRecord changeRecord = new DeleteChangeRecord(asset); changeRecord.setAuthor(securityUtils.getCurrentUser().toUser()); changeRecord.setTimestamp(Utils.timestamp()); From 44541bf924035d748b8a23f72871746613ac8491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Ka=C5=88ka?= Date: Tue, 5 Nov 2024 14:44:35 +0100 Subject: [PATCH 6/9] [Enhancement kbss-cvut/termit-ui#530] Fix removePublishesEventAndDropsGraph test --- .../termit/persistence/dao/VocabularyDaoTest.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/test/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDaoTest.java b/src/test/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDaoTest.java index 23b72777c..02af22e4c 100644 --- a/src/test/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDaoTest.java +++ b/src/test/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDaoTest.java @@ -30,6 +30,7 @@ import cz.cvut.kbss.termit.event.AssetPersistEvent; import cz.cvut.kbss.termit.event.AssetUpdateEvent; import cz.cvut.kbss.termit.event.RefreshLastModifiedEvent; +import cz.cvut.kbss.termit.event.VocabularyEvent; import cz.cvut.kbss.termit.event.VocabularyWillBeRemovedEvent; import cz.cvut.kbss.termit.model.Glossary; import cz.cvut.kbss.termit.model.Model; @@ -761,10 +762,14 @@ void removePublishesEventAndDropsGraph() { transactional(() -> sut.remove(vocabulary)); - ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(VocabularyWillBeRemovedEvent.class); - verify(eventPublisher).publishEvent(eventCaptor.capture()); + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(VocabularyWillBeRemovedEvent.class); + verify(eventPublisher, atLeastOnce()).publishEvent(eventCaptor.capture()); - VocabularyWillBeRemovedEvent event = eventCaptor.getValue(); + VocabularyWillBeRemovedEvent event = (VocabularyWillBeRemovedEvent) eventCaptor + .getAllValues().stream() + .filter(e -> e instanceof VocabularyWillBeRemovedEvent) + .findAny().orElseThrow(); + assertNotNull(event); assertEquals(event.getVocabularyIri(), vocabulary.getUri()); From f3f6dafb9ec4abc962301386cf321a73f452af95 Mon Sep 17 00:00:00 2001 From: Martin Ledvinka Date: Mon, 11 Nov 2024 16:14:46 +0100 Subject: [PATCH 7/9] [GH-309] Allow running in development mode without configuring mail server. --- .../kbss/termit/service/mail/Postman.java | 11 ++++- .../cz/cvut/kbss/termit/util/Constants.java | 45 +++++++------------ .../java/cz/cvut/kbss/termit/util/Utils.java | 43 +++++++++++------- 3 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/main/java/cz/cvut/kbss/termit/service/mail/Postman.java b/src/main/java/cz/cvut/kbss/termit/service/mail/Postman.java index cb66781e9..04cd590ce 100644 --- a/src/main/java/cz/cvut/kbss/termit/service/mail/Postman.java +++ b/src/main/java/cz/cvut/kbss/termit/service/mail/Postman.java @@ -19,6 +19,7 @@ import cz.cvut.kbss.termit.exception.PostmanException; import cz.cvut.kbss.termit.exception.ValidationException; +import cz.cvut.kbss.termit.util.Utils; import jakarta.mail.MessagingException; import jakarta.mail.internet.InternetAddress; import jakarta.mail.internet.MimeMessage; @@ -65,7 +66,12 @@ public Postman(Environment env, @Autowired(required = false) JavaMailSender mail @PostConstruct public void postConstruct() { - if(mailSender == null) { + if (mailSender == null) { + if (Utils.isDevelopmentProfile(env.getActiveProfiles())) { + LOG.warn( + "Mail server not configured but running in development mode. Will not be able to send messages."); + return; + } throw new ValidationException("Mail server not configured."); } } @@ -86,7 +92,8 @@ public void sendMessage(Message message) { final MimeMessage mail = mailSender.createMimeMessage(); final MimeMessageHelper helper = new MimeMessageHelper(mail, true); - helper.setFrom(new InternetAddress(sender != null ? sender : senderUsername, FROM_NICKNAME, StandardCharsets.UTF_8.toString())); + helper.setFrom(new InternetAddress(sender != null ? sender : senderUsername, FROM_NICKNAME, + StandardCharsets.UTF_8.toString())); helper.setTo(message.getRecipients().toArray(new String[]{})); helper.setSubject(message.getSubject()); helper.setText(message.getContent(), true); diff --git a/src/main/java/cz/cvut/kbss/termit/util/Constants.java b/src/main/java/cz/cvut/kbss/termit/util/Constants.java index 5d7ead6a9..7cb925992 100644 --- a/src/main/java/cz/cvut/kbss/termit/util/Constants.java +++ b/src/main/java/cz/cvut/kbss/termit/util/Constants.java @@ -153,6 +153,23 @@ public class Constants { "Notation", "Example", "References") ); + + /** + * the maximum amount of data to buffer when sending messages to a WebSocket session + */ + public static final int WEBSOCKET_SEND_BUFFER_SIZE_LIMIT = Integer.MAX_VALUE; + + /** + * Set the maximum time allowed in milliseconds after the WebSocket connection is established + * and before the first sub-protocol message is received. + */ + public static final int WEBSOCKET_TIME_TO_FIRST_MESSAGE = 15 * 1000 /* 15s */; + + /** + * Development Spring profile. + */ + public static final String DEVELOPMENT_PROFILE = "development"; + private Constants() { throw new AssertionError(); } @@ -247,32 +264,4 @@ private QueryParams() { throw new AssertionError(); } } - - public static final class DebouncingGroups { - - /** - * Text analysis of all terms in specific vocabulary - */ - public static final String TEXT_ANALYSIS_VOCABULARY_TERMS_ALL_DEFINITIONS = "TEXT_ANALYSIS_VOCABULARY_TERMS_ALL_DEFINITIONS"; - - /** - * Text analysis of all vocabularies - */ - public static final String TEXT_ANALYSIS_VOCABULARY = "TEXT_ANALYSIS_VOCABULARY"; - - private DebouncingGroups() { - throw new AssertionError(); - } - } - - /** - * the maximum amount of data to buffer when sending messages to a WebSocket session - */ - public static final int WEBSOCKET_SEND_BUFFER_SIZE_LIMIT = Integer.MAX_VALUE; - - /** - * Set the maximum time allowed in milliseconds after the WebSocket connection is established - * and before the first sub-protocol message is received. - */ - public static final int WEBSOCKET_TIME_TO_FIRST_MESSAGE = 15 * 1000 /* 15s */; } diff --git a/src/main/java/cz/cvut/kbss/termit/util/Utils.java b/src/main/java/cz/cvut/kbss/termit/util/Utils.java index f8857028d..7adf76742 100644 --- a/src/main/java/cz/cvut/kbss/termit/util/Utils.java +++ b/src/main/java/cz/cvut/kbss/termit/util/Utils.java @@ -44,6 +44,7 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -194,13 +195,20 @@ public static String getVocabularyIri(final Set conceptUris, String term if (conceptUris.isEmpty()) { throw new IllegalArgumentException("No namespace candidate."); } - final Iterator i = conceptUris.iterator(); - final String conceptUri = i.next(); + final String namespace = extractNamespace(termSeparator, conceptUri); + for (final String s : conceptUris) { + if (!s.startsWith(namespace)) { + throw new IllegalArgumentException( + "Not all Concept IRIs have the same namespace: " + conceptUri + " vs. " + namespace); + } + } + return namespace; + } + private static String extractNamespace(String termSeparator, String conceptUri) { final String separator; - if (conceptUri.lastIndexOf(termSeparator) > 0) { separator = termSeparator; } else if (conceptUri.lastIndexOf("#") > 0) { @@ -210,16 +218,7 @@ public static String getVocabularyIri(final Set conceptUris, String term } else { throw new IllegalArgumentException("The IRI does not have a proper format: " + conceptUri); } - - final String namespace = conceptUri.substring(0, conceptUri.lastIndexOf(separator)); - - for (final String s : conceptUris) { - if (!s.startsWith(namespace)) { - throw new IllegalArgumentException( - "Not all Concept IRIs have the same namespace: " + conceptUri + " vs. " + namespace); - } - } - return namespace; + return conceptUri.substring(0, conceptUri.lastIndexOf(separator)); } /** @@ -402,15 +401,25 @@ public static void pruneBlankTranslations(MultilingualString str) { /** * Converts the map into a string - * @return Empty string when the map is {@code null}, otherwise the String in format - * {@code {key=value, key=value}} + * + * @return Empty string when the map is {@code null}, otherwise the String in format {@code {key=value, key=value}} */ public static String mapToString(Map map) { if (map == null) { return ""; } return map.keySet().stream() - .map(key -> key + "=" + map.get(key)) - .collect(Collectors.joining(", ", "{", "}")); + .map(key -> key + "=" + map.get(key)) + .collect(Collectors.joining(", ", "{", "}")); + } + + /** + * Checks whether the {@code development} profile is active. + * + * @param activeProfiles Array of active profiles + * @return {@code true} if the {@code development} profile is active, {@code false} otherwise + */ + public static boolean isDevelopmentProfile(String[] activeProfiles) { + return Arrays.binarySearch(activeProfiles, Constants.DEVELOPMENT_PROFILE) != -1; } } From 5fbccba0d7b67711c9535693cfeca0d563a06da4 Mon Sep 17 00:00:00 2001 From: Martin Ledvinka Date: Wed, 13 Nov 2024 16:30:00 +0100 Subject: [PATCH 8/9] [GH-309] Remove text analysis service URL from application.yml It is not necessary in development. When one wants to test TermIt with text analysis, they should configure the real URL of the service. --- src/main/resources/application.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8d9cae801..655043d51 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -66,8 +66,6 @@ termit: separator: /verze file: storage: /tmp/termit - textAnalysis: - url: http://localhost:8081/annotace/annotate changetracking: context: extension: /zmeny From 742a0174dcdc0da9826249710cb050dd70be0255 Mon Sep 17 00:00:00 2001 From: Martin Ledvinka Date: Wed, 13 Nov 2024 16:32:01 +0100 Subject: [PATCH 9/9] [GH-309] Document the development Spring profile. --- doc/setup.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/setup.md b/doc/setup.md index 839fe0b3c..af068c823 100644 --- a/doc/setup.md +++ b/doc/setup.md @@ -39,6 +39,7 @@ by the application: * `lucene` - decides whether Lucene text indexing is enabled and should be used in full text search queries. * `admin-registration-only` - decides whether new users can be registered only by application admin, or whether anyone can register. * `no-cache` - disables Ehcache, which is used to cache lists of resources and vocabularies for faster retrieval, and persistence cache. +* `development` - indicates that the application is running is development. This, for example, means that mail server does not need to be configured. The `lucene` Spring profile is activated automatically by the `graphdb` Maven. `admin-registration-only` and `no-cache` have to be added either in `application.yml` directly, or one can pass the parameter to Maven build, e.g.: