From 31d6306a973e47d5d9df0e54966875c285ea3628 Mon Sep 17 00:00:00 2001 From: SrdjanStevanetic Date: Thu, 21 Mar 2024 17:01:32 +0100 Subject: [PATCH 1/3] debias annotations (parse targets, integration tests, solr adjustments) --- .../definitions/model/Annotation.java | 5 + .../factory/impl/SelectorObjectFactory.java | 8 ++ .../model/impl/AbstractAnnotation.java | 24 ++++ .../selector/RDFStatementSelector.java | 16 +++ .../resource/selector/TextQuoteSelector.java | 15 ++ .../impl/BaseRDFStatementSelector.java | 55 +++++++ .../selector/impl/BaseTextQuoteSelector.java | 49 +++++++ .../model/vocabulary/SelectorTypes.java | 2 +- .../model/vocabulary/WebAnnotationFields.java | 3 + .../fields/WebAnnotationModelFields.java | 9 ++ .../mongo/model/PersistentAnnotationImpl.java | 14 +- .../model/internal/SolrAnnotationImpl.java | 1 + .../impl/SolrAnnotationServiceImpl.java | 16 +++ .../service/impl/SolrAnnotationUtils.java | 33 +++-- .../tests/AnnotationTestsConstants.java | 4 +- .../web/AnnotationCreateHighlightingIT.java | 60 ++++++++ .../resources/highlighting/highlighting.json | 37 +++++ .../highlighting_without_exact.json | 33 +++++ .../highlighting_without_predicate.json | 36 +++++ .../utils/parse/AnnotationLdParser.java | 135 ++++++++++++++++-- .../validation/BaseAnnotationValidator.java | 117 +++++++++++---- .../config/I18nConstantsAnnotation.java | 2 +- .../src/main/resources/messages_en.properties | 2 +- 23 files changed, 623 insertions(+), 53 deletions(-) create mode 100644 annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/RDFStatementSelector.java create mode 100644 annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/TextQuoteSelector.java create mode 100644 annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/impl/BaseRDFStatementSelector.java create mode 100644 annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/impl/BaseTextQuoteSelector.java create mode 100644 annotation-tests/src/integration-test/java/eu/europeana/annotation/tests/web/AnnotationCreateHighlightingIT.java create mode 100644 annotation-tests/src/integration-test/resources/highlighting/highlighting.json create mode 100644 annotation-tests/src/integration-test/resources/highlighting/highlighting_without_exact.json create mode 100644 annotation-tests/src/integration-test/resources/highlighting/highlighting_without_predicate.json diff --git a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/Annotation.java b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/Annotation.java index 0e1d14216..7b51f395b 100644 --- a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/Annotation.java +++ b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/Annotation.java @@ -1,6 +1,7 @@ package eu.europeana.annotation.definitions.model; import java.util.Date; +import java.util.List; import eu.europeana.annotation.definitions.model.agent.Agent; import eu.europeana.annotation.definitions.model.body.Body; @@ -31,6 +32,10 @@ public interface Annotation { public abstract void setTarget(Target target); public abstract Target getTarget(); + + public abstract void setTargets(List target); + + public abstract List getTargets(); public abstract void setBody(Body body); diff --git a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/factory/impl/SelectorObjectFactory.java b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/factory/impl/SelectorObjectFactory.java index 688c3ba50..c2ecc7f61 100644 --- a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/factory/impl/SelectorObjectFactory.java +++ b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/factory/impl/SelectorObjectFactory.java @@ -3,8 +3,10 @@ import eu.europeana.annotation.definitions.exception.AnnotationAttributeInstantiationException; import eu.europeana.annotation.definitions.model.factory.AbstractModelObjectFactory; import eu.europeana.annotation.definitions.model.resource.selector.Selector; +import eu.europeana.annotation.definitions.model.resource.selector.impl.BaseRDFStatementSelector; import eu.europeana.annotation.definitions.model.resource.selector.impl.BaseSvgSelector; import eu.europeana.annotation.definitions.model.resource.selector.impl.BaseTextPositionSelector; +import eu.europeana.annotation.definitions.model.resource.selector.impl.BaseTextQuoteSelector; import eu.europeana.annotation.definitions.model.resource.selector.impl.SvgRectangleSelector; import eu.europeana.annotation.definitions.model.vocabulary.SelectorTypes; @@ -35,6 +37,12 @@ public Class getClassForType(Enum modelType) case TEXT_POSITION_SELECTOR: returnType = BaseTextPositionSelector.class; break; + case TEXT_QUOTE_SELECTOR: + returnType = BaseTextQuoteSelector.class; + break; + case RDF_STATEMENT_SELECTOR: + returnType = BaseRDFStatementSelector.class; + break; case SVG_RECTANGLE_SELECTOR: returnType = SvgRectangleSelector.class; break; diff --git a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/impl/AbstractAnnotation.java b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/impl/AbstractAnnotation.java index 6370c21d4..6bc245e2e 100644 --- a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/impl/AbstractAnnotation.java +++ b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/impl/AbstractAnnotation.java @@ -1,7 +1,10 @@ package eu.europeana.annotation.definitions.model.impl; import java.util.Date; +import java.util.List; + import org.apache.commons.lang3.StringUtils; + import eu.europeana.annotation.definitions.model.Annotation; import eu.europeana.annotation.definitions.model.agent.Agent; import eu.europeana.annotation.definitions.model.body.Body; @@ -23,6 +26,7 @@ public abstract class AbstractAnnotation implements Annotation, AnnotationView { private Date generated; private Body body; private Target target; + private List targets; protected String motivation; private Style styledBy; protected MotivationTypes motivationType; @@ -80,6 +84,11 @@ public boolean equals(Object other) { && (!this.getTarget().equals(that.getTarget()))) { res = false; } + + if ((this.getTargets() != null) && (that.getTargets() != null) + && (!this.getTargets().equals(that.getTargets()))) { + res = false; + } if ((this.getMotivation() != null) && (that.getMotivation() != null) && (!this.getMotivation().equals(that.getMotivation()))) { @@ -151,6 +160,11 @@ public boolean equalsContent(Object other) { res = false; } + if ((this.getTargets() != null) && (that.getTargets() != null) + && (!this.getTargets().equals(that.getTargets()))) { + res = false; + } + if ((this.getMotivation() != null) && (that.getMotivation() != null) && (!this.getMotivation().equals(that.getMotivation()))) { res = false; @@ -230,6 +244,16 @@ public void setTarget(Target target) { this.target = target; } + @Override + public List getTargets() { + return targets; + } + + @Override + public void setTargets(List targets) { + this.targets = targets; + } + @Override public String getMotivation() { return motivation; diff --git a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/RDFStatementSelector.java b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/RDFStatementSelector.java new file mode 100644 index 000000000..b0298e10f --- /dev/null +++ b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/RDFStatementSelector.java @@ -0,0 +1,16 @@ +package eu.europeana.annotation.definitions.model.resource.selector; + +public interface RDFStatementSelector extends Selector { + + public abstract String getHasPredicate(); + public abstract void setHasPredicate(String hasPredicate); + + public String getHasSubject(); + public void setHasSubject(String hasSubject); + + public String getHasObject(); + public void setHasObject(String hasObject); + + public abstract TextQuoteSelector getRefinedBy(); + public abstract void setRefinedBy(TextQuoteSelector refinedBy); +} diff --git a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/TextQuoteSelector.java b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/TextQuoteSelector.java new file mode 100644 index 000000000..da5175918 --- /dev/null +++ b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/TextQuoteSelector.java @@ -0,0 +1,15 @@ +package eu.europeana.annotation.definitions.model.resource.selector; + +import java.util.Map; + +public interface TextQuoteSelector extends Selector { + + public abstract String getPrefix(); + public abstract void setPrefix(String prefix); + + public abstract String getSuffix(); + public abstract void setSuffix(String suffix); + + public abstract Map getExact(); + public abstract void setExact(Map exact); +} diff --git a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/impl/BaseRDFStatementSelector.java b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/impl/BaseRDFStatementSelector.java new file mode 100644 index 000000000..7b7ad3611 --- /dev/null +++ b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/impl/BaseRDFStatementSelector.java @@ -0,0 +1,55 @@ +package eu.europeana.annotation.definitions.model.resource.selector.impl; + +import eu.europeana.annotation.definitions.model.resource.selector.RDFStatementSelector; +import eu.europeana.annotation.definitions.model.resource.selector.TextQuoteSelector; +import eu.europeana.annotation.definitions.model.vocabulary.WebAnnotationFields; + +public class BaseRDFStatementSelector extends BaseSelector implements RDFStatementSelector{ + + public BaseRDFStatementSelector() { + super(); + this.setSelectorType(WebAnnotationFields.RDF_STATEMENT_SELECTOR); + } + + private String hasPredicate; + private String hasSubject; + private String hasObject; + private TextQuoteSelector refinedBy; + + @Override + public String getHasPredicate() { + return hasPredicate; + } + @Override + public void setHasPredicate(String hasPredicate) { + this.hasPredicate=hasPredicate; + } + + @Override + public String getHasSubject() { + return hasSubject; + } + @Override + public void setHasSubject(String hasSubject) { + this.hasSubject = hasSubject; + } + + @Override + public String getHasObject() { + return hasObject; + } + @Override + public void setHasObject(String hasObject) { + this.hasObject = hasObject; + } + + @Override + public TextQuoteSelector getRefinedBy() { + return refinedBy; + } + @Override + public void setRefinedBy(TextQuoteSelector refinedBy) { + this.refinedBy=refinedBy; + } + +} diff --git a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/impl/BaseTextQuoteSelector.java b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/impl/BaseTextQuoteSelector.java new file mode 100644 index 000000000..c16587f4f --- /dev/null +++ b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/resource/selector/impl/BaseTextQuoteSelector.java @@ -0,0 +1,49 @@ +package eu.europeana.annotation.definitions.model.resource.selector.impl; + +import java.util.Map; + +import eu.europeana.annotation.definitions.model.resource.selector.TextQuoteSelector; +import eu.europeana.annotation.definitions.model.vocabulary.WebAnnotationFields; + +public class BaseTextQuoteSelector extends BaseSelector implements TextQuoteSelector{ + + public BaseTextQuoteSelector() { + super(); + this.setSelectorType(WebAnnotationFields.TEXT_QUOTE_SELECTOR); + } + + private String prefix; + private String suffix; + private Map exact; + + @Override + public String getPrefix() { + return prefix; + } + + @Override + public void setPrefix(String prefix) { + this.prefix=prefix; + } + + @Override + public String getSuffix() { + return suffix; + } + + @Override + public void setSuffix(String suffix) { + this.suffix=suffix; + } + + @Override + public Map getExact() { + return exact; + } + + @Override + public void setExact(Map exact) { + this.exact=exact; + } + +} diff --git a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/vocabulary/SelectorTypes.java b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/vocabulary/SelectorTypes.java index 5b2c05cfe..7e82a14fe 100644 --- a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/vocabulary/SelectorTypes.java +++ b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/vocabulary/SelectorTypes.java @@ -1,5 +1,5 @@ package eu.europeana.annotation.definitions.model.vocabulary; public enum SelectorTypes { - DATA_POSITION_SELECTOR, FRAGMENT_SELECTOR, SVG_SELECTOR, TEXT_POSITION_SELECTOR, TEXT_QUOTE_SELECTOR, SVG_RECTANGLE_SELECTOR + DATA_POSITION_SELECTOR, FRAGMENT_SELECTOR, SVG_SELECTOR, TEXT_POSITION_SELECTOR, TEXT_QUOTE_SELECTOR, RDF_STATEMENT_SELECTOR, SVG_RECTANGLE_SELECTOR } diff --git a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/vocabulary/WebAnnotationFields.java b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/vocabulary/WebAnnotationFields.java index 5610a8dc8..d8af407bb 100644 --- a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/vocabulary/WebAnnotationFields.java +++ b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/vocabulary/WebAnnotationFields.java @@ -148,6 +148,9 @@ public interface WebAnnotationFields extends WebAnnotationModelFields{ public static final String SCOPE = "scope"; public static final String SOURCE = "source"; + public static final String SELECTOR = "selector"; + public static final String RDF_STATEMENT_SELECTOR = "RDFStatementSelector"; + public static final String TEXT_QUOTE_SELECTOR = "TextQuoteSelector"; public static final String RIGHTS = "edmRights"; diff --git a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/vocabulary/fields/WebAnnotationModelFields.java b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/vocabulary/fields/WebAnnotationModelFields.java index 45373f324..b00c33e54 100644 --- a/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/vocabulary/fields/WebAnnotationModelFields.java +++ b/annotation-definitions/src/main/java/eu/europeana/annotation/definitions/model/vocabulary/fields/WebAnnotationModelFields.java @@ -94,5 +94,14 @@ public interface WebAnnotationModelFields { public static final String COUNTRY_NAME = "countryName"; public static final String COMMA = ","; + + // selector fields + public static final String HAS_PREDICATE="hasPredicate"; + public static final String REFINED_BY="refinedBy"; + public static final String EXACT="exact"; + public static final String EXACT_VALUE="@value"; + public static final String EXACT_LANGUAGE="@language"; + public static final String PREFIX="prefix"; + public static final String SUFFIX="suffix"; } diff --git a/annotation-mongo/src/main/java/eu/europeana/annotation/mongo/model/PersistentAnnotationImpl.java b/annotation-mongo/src/main/java/eu/europeana/annotation/mongo/model/PersistentAnnotationImpl.java index a662f6a98..43a511d27 100644 --- a/annotation-mongo/src/main/java/eu/europeana/annotation/mongo/model/PersistentAnnotationImpl.java +++ b/annotation-mongo/src/main/java/eu/europeana/annotation/mongo/model/PersistentAnnotationImpl.java @@ -1,6 +1,8 @@ package eu.europeana.annotation.mongo.model; import java.util.Date; +import java.util.List; + import org.bson.types.ObjectId; import org.mongodb.morphia.annotations.Embedded; import org.mongodb.morphia.annotations.Entity; @@ -42,7 +44,9 @@ public class PersistentAnnotationImpl implements PersistentAnnotation, Persisten private Body body; @Embedded private Target target; - + @Embedded + private List targets; + @Property(PersistentAnnotation.FIELD_MOTIVATION) private String motivation; private Style styledBy; @@ -101,6 +105,14 @@ public void setTarget(Target target) { this.target = target; } + public List getTargets() { + return targets; + } + + public void setTargets(List targets) { + this.targets = targets; + } + public String getMotivation() { return motivation; } diff --git a/annotation-solr/src/main/java/eu/europeana/annotation/solr/model/internal/SolrAnnotationImpl.java b/annotation-solr/src/main/java/eu/europeana/annotation/solr/model/internal/SolrAnnotationImpl.java index 596079f4e..e11c7f412 100644 --- a/annotation-solr/src/main/java/eu/europeana/annotation/solr/model/internal/SolrAnnotationImpl.java +++ b/annotation-solr/src/main/java/eu/europeana/annotation/solr/model/internal/SolrAnnotationImpl.java @@ -86,6 +86,7 @@ public SolrAnnotationImpl(Annotation annotation, Summary summary, String annoBas this.setModerationScore(summary.getScore()); this.setTarget(annotation.getTarget()); + this.setTargets(annotation.getTargets()); this.setBody(annotation.getBody()); this.setScenario(findScenarioType(annotation)); diff --git a/annotation-solr/src/main/java/eu/europeana/annotation/solr/service/impl/SolrAnnotationServiceImpl.java b/annotation-solr/src/main/java/eu/europeana/annotation/solr/service/impl/SolrAnnotationServiceImpl.java index 2f646c830..d0a4b4fcb 100644 --- a/annotation-solr/src/main/java/eu/europeana/annotation/solr/service/impl/SolrAnnotationServiceImpl.java +++ b/annotation-solr/src/main/java/eu/europeana/annotation/solr/service/impl/SolrAnnotationServiceImpl.java @@ -532,6 +532,9 @@ else if(BodyInternalTypes.isSimpleTagBody(anno.getBody().getInternalType())) { case LINKFORCONTRIBUTING : queries.add(solrUniquenessQueryLinkForContributing(anno, noSelfDupplicate)); break; + case HIGHLIGHTING : + queries.add(solrUniquenessQueryDebias(anno, noSelfDupplicate)); + break; default: break; } @@ -606,6 +609,19 @@ private SolrQuery solrUniquenessQueryCaptionsAndSubtitles(Annotation anno, boole return query; } + private SolrQuery solrUniquenessQueryDebias(Annotation anno, boolean noSelfDupplicate) { + SolrQuery query = new SolrQuery(); + query.setQuery(WebAnnotationModelFields.MOTIVATION + ":\"" + MotivationTypes.HIGHLIGHTING.getOaType() + "\""); + query.addFilterQuery(SolrAnnotationConstants.TARGET_URI + ":\"" + anno.getTarget().getSource() + "\""); + List bodyUris = extractUriValues(anno.getBody()); + for (int i=0; i 0) { query.addFilterQuery("-" + SolrAnnotationConstants.ANNO_ID + ":" + anno.getIdentifier()); diff --git a/annotation-solr/src/main/java/eu/europeana/annotation/solr/service/impl/SolrAnnotationUtils.java b/annotation-solr/src/main/java/eu/europeana/annotation/solr/service/impl/SolrAnnotationUtils.java index 39b111288..a2cfc3706 100644 --- a/annotation-solr/src/main/java/eu/europeana/annotation/solr/service/impl/SolrAnnotationUtils.java +++ b/annotation-solr/src/main/java/eu/europeana/annotation/solr/service/impl/SolrAnnotationUtils.java @@ -5,12 +5,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.response.FacetField; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrException; + import eu.europeana.annotation.definitions.model.Annotation; import eu.europeana.annotation.definitions.model.body.Body; import eu.europeana.annotation.definitions.model.body.GraphBody; @@ -204,17 +206,26 @@ else if (body.getValues() != null) { } protected void processTargetUris(SolrAnnotation solrAnnotation) { - - SpecificResource internetResource = solrAnnotation.getTarget(); - // extract URIs for target_uri field - List targetUris = extractUriValues(internetResource); - solrAnnotation.setTargetUris(targetUris); - - if(targetUris!=null) { - // Extract URIs for target_record_id - List recordIds = extractRecordIds(targetUris); - solrAnnotation.setTargetRecordIds(recordIds); - } + SpecificResource internetResource = null; + if(solrAnnotation.getTarget()!=null) { + internetResource=solrAnnotation.getTarget(); + } + else if(solrAnnotation.getTargets()!=null && solrAnnotation.getTargets().size()>0) { + /* + * in case of multiple targets, they all have the same uri, e.g. a source (e.g. in case of the debias targets), + * so we only process the first target + */ + internetResource=solrAnnotation.getTargets().get(0); + } + + // extract URIs for target_uri field + List targetUris = extractUriValues(internetResource); + if(! targetUris.isEmpty()) { + solrAnnotation.setTargetUris(targetUris); + // Extract URIs for target_record_id + List recordIds = extractRecordIds(targetUris); + solrAnnotation.setTargetRecordIds(recordIds); + } } protected List extractUriValues(SpecificResource specificResource) { diff --git a/annotation-tests/src/integration-test/java/eu/europeana/annotation/tests/AnnotationTestsConstants.java b/annotation-tests/src/integration-test/java/eu/europeana/annotation/tests/AnnotationTestsConstants.java index f4a441464..9b2608c02 100644 --- a/annotation-tests/src/integration-test/java/eu/europeana/annotation/tests/AnnotationTestsConstants.java +++ b/annotation-tests/src/integration-test/java/eu/europeana/annotation/tests/AnnotationTestsConstants.java @@ -116,7 +116,9 @@ public String get_TAG_STANDARD_TEST_VALUE_TARGET(String itemDataEndpoint) { public static final String LINK_FOR_CONTRIBUTING_TARGET_SPECIFIC = "/linkforcontributing/link_for_contributing_target_specific.json"; public static final String LINK_FOR_CONTRIBUTING_TARGET_SPECIFIC_ID = "/linkforcontributing/link_for_contributing_target_specific_id.json"; - + public static final String HIGHLIGHTING = "/highlighting/highlighting.json"; + public static final String HIGHLIGHTING_WITHOUT_PREDICATE = "/highlighting/highlighting_without_predicate.json"; + public static final String HIGHLIGHTING_WITHOUT_EXACT = "/highlighting/highlighting_without_exact.json"; public static final String START = "{"; public static final String END = "}"; diff --git a/annotation-tests/src/integration-test/java/eu/europeana/annotation/tests/web/AnnotationCreateHighlightingIT.java b/annotation-tests/src/integration-test/java/eu/europeana/annotation/tests/web/AnnotationCreateHighlightingIT.java new file mode 100644 index 000000000..d1a01c0e8 --- /dev/null +++ b/annotation-tests/src/integration-test/java/eu/europeana/annotation/tests/web/AnnotationCreateHighlightingIT.java @@ -0,0 +1,60 @@ +package eu.europeana.annotation.tests.web; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.apache.stanbol.commons.exception.JsonParseException; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import eu.europeana.annotation.definitions.model.Annotation; +import eu.europeana.annotation.definitions.model.vocabulary.MotivationTypes; +import eu.europeana.annotation.tests.AbstractIntegrationTest; +import eu.europeana.annotation.tests.utils.AnnotationTestUtils; + +@SpringBootTest +@AutoConfigureMockMvc +class AnnotationCreateHighlightingIT extends AbstractIntegrationTest { + + protected Annotation parseHighlighting(String jsonString) throws JsonParseException { + MotivationTypes motivationType = MotivationTypes.HIGHLIGHTING; + return AnnotationTestUtils.parseAnnotation(jsonString, motivationType); + } + + @Test + void createHighlighting() throws Exception { + String requestBody = AnnotationTestUtils.getJsonStringInput(HIGHLIGHTING); + Annotation inputAnno = parseHighlighting(requestBody); + + Annotation storedAnno = createTestAnnotation(HIGHLIGHTING, true); + addToCreatedAnnotations(storedAnno.getIdentifier()); + + // validate the reflection of input in output! + AnnotationTestUtils.validateOutputAgainstInput(storedAnno, inputAnno); + } + + @Test + void createHighlightingWihoutPredicate() throws Exception { + ResponseEntity response = storeTestAnnotation(HIGHLIGHTING_WITHOUT_PREDICATE, true); + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + } + + @Test + void createHighlightingWihoutExact() throws Exception { + ResponseEntity response = storeTestAnnotation(HIGHLIGHTING_WITHOUT_EXACT, true); + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + } + + @Test + void checkAnnotationDuplicatesCreateHighlighting() throws Exception { + ResponseEntity response = storeTestAnnotation(HIGHLIGHTING, true); + assertEquals(HttpStatus.CREATED, response.getStatusCode()); + Annotation storedAnno = AnnotationTestUtils.parseResponseBody(response); + addToCreatedAnnotations(storedAnno.getIdentifier()); + response = storeTestAnnotation(HIGHLIGHTING, true, null); + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + } + +} diff --git a/annotation-tests/src/integration-test/resources/highlighting/highlighting.json b/annotation-tests/src/integration-test/resources/highlighting/highlighting.json new file mode 100644 index 000000000..874dac2f4 --- /dev/null +++ b/annotation-tests/src/integration-test/resources/highlighting/highlighting.json @@ -0,0 +1,37 @@ +{ + "motivation": "highlighting", + "body": "http://debias.example.org/term/xpto", + "target": [ + { + "source": "http://data.europeana.eu/item/some_cho", + "selector":{ + "type" : "RDFStatementSelector", + "hasPredicate": "dc:title", + "refinedBy" :{ + "type" : "TextQuoteSelector", + "exact": { + "@value": "bias term", + "@language": "en" + }, + "prefix": "same " + } + } + }, + { + "source": "http://data.europeana.eu/item/some_cho", + "selector": { + "type" : "RDFStatementSelector", + "hasPredicate": "dc:description", + "refinedBy" : { + "type" : "TextQuoteSelector", + "exact": { + "@value": "bias term", + "@language": "en" + }, + "prefix": "with a ", + "suffix": ". Also" + } + } + } + ] +} diff --git a/annotation-tests/src/integration-test/resources/highlighting/highlighting_without_exact.json b/annotation-tests/src/integration-test/resources/highlighting/highlighting_without_exact.json new file mode 100644 index 000000000..d447917b7 --- /dev/null +++ b/annotation-tests/src/integration-test/resources/highlighting/highlighting_without_exact.json @@ -0,0 +1,33 @@ +{ + "motivation": "highlighting", + "body": "http://debias.example.org/term/xpto", + "target": [ + { + "source": "http://data.europeana.eu/item/some_cho", + "selector":{ + "type" : "RDFStatementSelector", + "hasPredicate": "dc:title", + "refinedBy" :{ + "type" : "TextQuoteSelector", + "exact": { + "@value": "bias term", + "@language": "en" + }, + "prefix": "same " + } + } + }, + { + "source": "http://data.europeana.eu/item/some_cho", + "selector": { + "type" : "RDFStatementSelector", + "hasPredicate": "dc:description", + "refinedBy" : { + "type" : "TextQuoteSelector", + "prefix": "with a ", + "suffix": ". Also" + } + } + } + ] +} diff --git a/annotation-tests/src/integration-test/resources/highlighting/highlighting_without_predicate.json b/annotation-tests/src/integration-test/resources/highlighting/highlighting_without_predicate.json new file mode 100644 index 000000000..ae0d64d9f --- /dev/null +++ b/annotation-tests/src/integration-test/resources/highlighting/highlighting_without_predicate.json @@ -0,0 +1,36 @@ +{ + "motivation": "highlighting", + "body": "http://debias.example.org/term/xpto", + "target": [ + { + "source": "http://data.europeana.eu/item/some_cho", + "selector":{ + "type" : "RDFStatementSelector", + "refinedBy" :{ + "type" : "TextQuoteSelector", + "exact": { + "@value": "bias term", + "@language": "en" + }, + "prefix": "same " + } + } + }, + { + "source": "http://data.europeana.eu/item/some_cho", + "selector": { + "type" : "RDFStatementSelector", + "hasPredicate": "dc:description", + "refinedBy" : { + "type" : "TextQuoteSelector", + "exact": { + "@value": "bias term", + "@language": "en" + }, + "prefix": "with a ", + "suffix": ". Also" + } + } + } + ] +} diff --git a/annotation-utils/src/main/java/eu/europeana/annotation/utils/parse/AnnotationLdParser.java b/annotation-utils/src/main/java/eu/europeana/annotation/utils/parse/AnnotationLdParser.java index adea58777..8de3ef5f8 100644 --- a/annotation-utils/src/main/java/eu/europeana/annotation/utils/parse/AnnotationLdParser.java +++ b/annotation-utils/src/main/java/eu/europeana/annotation/utils/parse/AnnotationLdParser.java @@ -33,10 +33,13 @@ import eu.europeana.annotation.definitions.model.factory.impl.AgentObjectFactory; import eu.europeana.annotation.definitions.model.factory.impl.AnnotationObjectFactory; import eu.europeana.annotation.definitions.model.factory.impl.BodyObjectFactory; +import eu.europeana.annotation.definitions.model.factory.impl.SelectorObjectFactory; import eu.europeana.annotation.definitions.model.factory.impl.TargetObjectFactory; import eu.europeana.annotation.definitions.model.resource.InternetResource; import eu.europeana.annotation.definitions.model.resource.SpecificResource; import eu.europeana.annotation.definitions.model.resource.impl.BaseInternetResource; +import eu.europeana.annotation.definitions.model.resource.selector.impl.BaseRDFStatementSelector; +import eu.europeana.annotation.definitions.model.resource.selector.impl.BaseTextQuoteSelector; import eu.europeana.annotation.definitions.model.target.Target; import eu.europeana.annotation.definitions.model.utils.AnnotationIdHelper; import eu.europeana.annotation.definitions.model.utils.TypeUtils; @@ -45,8 +48,10 @@ import eu.europeana.annotation.definitions.model.vocabulary.ContextTypes; import eu.europeana.annotation.definitions.model.vocabulary.MotivationTypes; import eu.europeana.annotation.definitions.model.vocabulary.ResourceTypes; +import eu.europeana.annotation.definitions.model.vocabulary.SelectorTypes; import eu.europeana.annotation.definitions.model.vocabulary.TargetTypes; import eu.europeana.annotation.definitions.model.vocabulary.WebAnnotationFields; +import eu.europeana.annotation.definitions.model.vocabulary.fields.WebAnnotationModelFields; import eu.europeana.annotation.definitions.model.vocabulary.fields.WebAnnotationModelKeywords; /** @@ -279,9 +284,7 @@ private void handleProperty(Annotation anno, JSONObject jo, String property) case WebAnnotationFields.TARGET: // TODO: move the parsing of ID and target in the upper method to // avoid redundant computations - Target target = parseTarget(valueObject); - target.setInputString(valueObject.toString()); - anno.setTarget(target); + parseTarget(anno, valueObject); break; case (WebAnnotationFields.EQUIVALENT_TO): anno.setEquivalentTo((String) valueObject); @@ -342,13 +345,51 @@ private Agent parseCreator(Object valueObject) throws JsonParseException { return parseAgent(AgentTypes.PERSON, valueObject); } - private Target parseTarget(Object valueObject) throws JsonParseException { - if (valueObject instanceof String) - return parseTarget(TargetTypes.WEB_PAGE.name(), (String) valueObject); - else if (valueObject instanceof JSONObject) - return parseTarget((JSONObject) valueObject); - else if (valueObject instanceof JSONArray) - return parseTarget((JSONArray) valueObject); + private List parseTargets(JSONArray valueObject) throws JsonParseException { + List targets=new ArrayList<>(); + try { + for(int i=0;i0) { + if(jsonArray.get(0) instanceof JSONObject) { + //in case we have an array of JSONObject objects, set multiple targets + List targets=parseTargets(jsonArray); + if(! targets.isEmpty()) { + anno.setTargets(targets); + } + } + else { + Target target= parseTarget((JSONArray) valueObject); + target.setInputString(valueObject.toString()); + anno.setTarget(target); + } + } + } else throw new JsonParseException("unsupported target+- deserialization for: " + valueObject); } @@ -663,6 +704,10 @@ private void parseSpecificResourceProperty(SpecificResource specificResource, St parseEuropeanaResourceId(specificResource, value); break; + case WebAnnotationFields.SELECTOR: + parseSelector(specificResource, value); + break; + case WebAnnotationFields.RIGHTS: specificResource.setEdmRights(value.toString()); break; @@ -672,6 +717,66 @@ private void parseSpecificResourceProperty(SpecificResource specificResource, St } } + + protected void parseSelector(SpecificResource specificResource, Object value) throws JsonParseException { + JSONObject json = (JSONObject) value; + try { + String selectorType=json.getString(WebAnnotationModelFields.TYPE); + switch(selectorType) { + case WebAnnotationFields.RDF_STATEMENT_SELECTOR: + parseRdfStatementSelector(specificResource, json); + break; + default: + break; + } + throw new AnnotationAttributeInstantiationException( + "Cannot find appropriate selector type for string: " + selectorType); + } + catch (JSONException e) { + throw new JsonParseException( + "unsupported json deserialization for the selector field with the json: " + json + " " + e.getMessage()); + } + + } + + private void parseRdfStatementSelector(SpecificResource specificResource, JSONObject json) throws JsonParseException { + try { + BaseRDFStatementSelector rdfStatementSelector = (BaseRDFStatementSelector) SelectorObjectFactory.getInstance().createObjectInstance(SelectorTypes.RDF_STATEMENT_SELECTOR); + + String hasPredicate=json.getString(WebAnnotationModelFields.HAS_PREDICATE); + JSONObject refinedBy=json.getJSONObject(WebAnnotationModelFields.REFINED_BY); + BaseTextQuoteSelector baseTextQuoteSelector = parseTextQuoteSelector(refinedBy); + + rdfStatementSelector.setHasPredicate(hasPredicate); + rdfStatementSelector.setRefinedBy(baseTextQuoteSelector); + + specificResource.setSelector(rdfStatementSelector); + } + catch (JSONException e) { + throw new JsonParseException( + "unsupported json deserialization for the RdfStatementSelector json representation: " + json + " " + e.getMessage()); + } + + } + + private BaseTextQuoteSelector parseTextQuoteSelector(JSONObject json) throws JsonParseException { + try { + BaseTextQuoteSelector baseTextQuoteSelector = (BaseTextQuoteSelector) SelectorObjectFactory.getInstance().createObjectInstance(SelectorTypes.TEXT_QUOTE_SELECTOR); + JSONObject exactJson=json.getJSONObject(WebAnnotationModelFields.EXACT); + Map exact=new HashMap<>(); + exact.put(WebAnnotationModelFields.EXACT_VALUE, exactJson.getString(WebAnnotationModelFields.EXACT_VALUE)); + exact.put(WebAnnotationModelFields.EXACT_LANGUAGE, exactJson.getString(WebAnnotationModelFields.EXACT_LANGUAGE)); + baseTextQuoteSelector.setExact(exact); + baseTextQuoteSelector.setPrefix(json.getString(WebAnnotationModelFields.PREFIX)); + baseTextQuoteSelector.setSuffix(json.getString(WebAnnotationModelFields.SUFFIX)); + return baseTextQuoteSelector; + } + catch (JSONException e) { + throw new JsonParseException( + "unsupported json deserialization for the TextQuoteSelector json representation: " + json + " " + e.getMessage()); + } + } + protected void parseEuropeanaResourceId(SpecificResource specificResource, Object value) { if(specificResource instanceof Target) { @@ -1002,7 +1107,9 @@ protected BodyInternalTypes guesBodyInternalType(MotivationTypes motivation, Str case TAGGING: return guesBodyTagSubType(value); case LINKFORCONTRIBUTING: - return BodyInternalTypes.SPECIFIC_RESOURCE; + return BodyInternalTypes.SPECIFIC_RESOURCE; + case HIGHLIGHTING: + return BodyInternalTypes.SEMANTIC_TAG; default: break; } @@ -1054,7 +1161,9 @@ else if (valueObject.has(WebAnnotationFields.VALUE)) return BodyInternalTypes.TAG; break; case LINKFORCONTRIBUTING: - return BodyInternalTypes.SPECIFIC_RESOURCE; + return BodyInternalTypes.SPECIFIC_RESOURCE; + case HIGHLIGHTING: + return BodyInternalTypes.SEMANTIC_TAG; default: break; @@ -1063,7 +1172,7 @@ else if (valueObject.has(WebAnnotationFields.VALUE)) throw new AnnotationAttributeInstantiationException( "Cannot find appropriate body type with MotivationType: " + motivation); } - + private BodyInternalTypes guesBodyTagSubType(String value) { // TODO: improve this .. similar check is done in validation.. these two // places should be merged. diff --git a/annotation-web/src/main/java/eu/europeana/annotation/web/validation/BaseAnnotationValidator.java b/annotation-web/src/main/java/eu/europeana/annotation/web/validation/BaseAnnotationValidator.java index d4b716d50..26b14437a 100644 --- a/annotation-web/src/main/java/eu/europeana/annotation/web/validation/BaseAnnotationValidator.java +++ b/annotation-web/src/main/java/eu/europeana/annotation/web/validation/BaseAnnotationValidator.java @@ -2,13 +2,17 @@ import java.io.IOException; import java.net.URI; +import java.util.List; import java.util.Set; + import javax.annotation.Resource; + import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpStatus; import org.springframework.lang.NonNull; import org.springframework.security.core.Authentication; import org.xml.sax.SAXParseException; + import eu.europeana.annotation.config.AnnotationConfiguration; import eu.europeana.annotation.definitions.model.Address; import eu.europeana.annotation.definitions.model.Annotation; @@ -19,6 +23,8 @@ import eu.europeana.annotation.definitions.model.body.impl.VcardAddressBody; import eu.europeana.annotation.definitions.model.entity.Place; import eu.europeana.annotation.definitions.model.resource.SpecificResource; +import eu.europeana.annotation.definitions.model.resource.selector.RDFStatementSelector; +import eu.europeana.annotation.definitions.model.resource.selector.Selector; import eu.europeana.annotation.definitions.model.target.Target; import eu.europeana.annotation.definitions.model.vocabulary.BodyInternalTypes; import eu.europeana.annotation.definitions.model.vocabulary.ResourceTypes; @@ -40,9 +46,17 @@ public abstract class BaseAnnotationValidator { - private static final String TRANSCRIPTION_TARGET_SOURCE = "transcription.target.source"; + private static final String TARGET_SOURCE = "target.source"; - private static final String TRANSCRIPTION_TARGET_SCOPE = "transcription.target.scope"; + private static final String TARGET_SCOPE = "target.scope"; + + private static final String TARGET_SELECTOR = "target.selector"; + + private static final String TARGET_SELECTOR_HAS_PREDICATE = "target.selector.hasPredicate"; + + private static final String TARGET_SELECTOR_HAS_SUBJECT = "target.selector.hasSubject"; + + private static final String TARGET_SELECTOR_REFINED_BY_EXACT = "target.selector.refinedBy.exact"; private static final String BODY_EDM_RIGHTS = "body.edmRights"; @@ -460,25 +474,28 @@ public void validateWebAnnotation(Annotation webAnnotation, Authentication authe switch (webAnnotation.getMotivationType()) { case LINKING: - validateLinking(webAnnotation); - break; + validateLinking(webAnnotation); + break; case DESCRIBING: - validateDescribing(webAnnotation); - break; + validateDescribing(webAnnotation); + break; case TAGGING: - validateTag(webAnnotation); - break; + validateTag(webAnnotation); + break; case TRANSCRIBING: case TRANSLATING: - validateTranscriptionOrTranslation(webAnnotation, authentication); - break; + validateTranscriptionOrTranslation(webAnnotation, authentication); + break; case SUBTITLING: case CAPTIONING: - validateSubtitleOrCaption(webAnnotation, authentication); - break; + validateSubtitleOrCaption(webAnnotation, authentication); + break; case LINKFORCONTRIBUTING: - validateLinkForContributing(webAnnotation); - break; + validateLinkForContributing(webAnnotation); + break; + case HIGHLIGHTING: + validateDebias(webAnnotation); + break; default: break; } @@ -636,6 +653,12 @@ protected void validateTranscriptionOrTranslation(Annotation webAnnotation, Auth // validate target validateTargetFields(webAnnotation.getTarget()); } + + protected void validateDebias(Annotation webAnnotation) throws PropertyValidationException { + validateBodyExists(webAnnotation.getBody()); + // validate targets + validateMultipleTargets(webAnnotation.getTargets()); + } /** * Validation of subtitle. @@ -737,6 +760,21 @@ private void validateBodyExists(Body body) throws PropertyValidationException { I18nConstantsAnnotation.MESSAGE_MISSING_MANDATORY_FIELD, new String[] {BODY}); } } + + private void validateMultipleTargets(List targets) throws PropertyValidationException { + if(targets==null || targets.isEmpty()) { + throw new PropertyValidationException(I18nConstantsAnnotation.MESSAGE_MISSING_MANDATORY_FIELD, + I18nConstantsAnnotation.MESSAGE_MISSING_MANDATORY_FIELD, new String[] {TARGET}); + } + /* for now only the specific resource can be in the multiple targets, + * so the same check as in the validateTargetFields method + */ + for(Target target : targets) { + if (target.getSource()!=null || target.getScope()!=null || target.getSelector()!=null) { + validateTargetSpecificResource(target); + } + } + } private void validateTargetFields(Target target) throws PropertyValidationException { // validates that the target value/values/scope (depending on what provided) has a valid base @@ -753,24 +791,29 @@ private void validateTargetFields(Target target) throws PropertyValidationExcept } else if (target.getValues() != null) { // validate multiple target values validateTargetMultipleValues(target); - } else if (target.getScope() != null || target.getSource() != null) { + } else if (target.getSource()!=null || target.getScope()!=null || target.getSelector()!=null) { // validate target for specific resource validateTargetSpecificResource(target); } } private void validateTargetSpecificResource(Target target) throws PropertyValidationException { - // scope and source must both be present in the target if one of them is present - if (target.getScope() == null) { - throw new PropertyValidationException(I18nConstantsAnnotation.MESSAGE_MISSING_MANDATORY_FIELD, - I18nConstantsAnnotation.MESSAGE_MISSING_MANDATORY_FIELD, - new String[] {TRANSCRIPTION_TARGET_SCOPE}); - } + // source must both be present in the target, and beside it (scope or selector) as well if (target.getSource() == null) { throw new PropertyValidationException(I18nConstantsAnnotation.MESSAGE_MISSING_MANDATORY_FIELD, I18nConstantsAnnotation.MESSAGE_MISSING_MANDATORY_FIELD, - new String[] {TRANSCRIPTION_TARGET_SOURCE}); + new String[] {TARGET_SOURCE}); } + if (target.getScope()==null && target.getSelector()==null) { + throw new PropertyValidationException(I18nConstantsAnnotation.MESSAGE_MISSING_MANDATORY_FIELD, + I18nConstantsAnnotation.MESSAGE_MISSING_MANDATORY_FIELD, + new String[] {TARGET_SCOPE + " or " + TARGET_SELECTOR}); + } + //validate target selectors + if(target.getSelector()!=null) { + validateTargetSelectors(target.getSelector()); + } + if (!target.getScope().contains(getConfiguration().getAnnoItemDataEndpoint())) { throw new PropertyValidationException( I18nConstantsAnnotation.ANNOTATION_INVALID_TARGET_BASE_URL, @@ -780,10 +823,36 @@ private void validateTargetSpecificResource(Target target) throws PropertyValida // target.source must be a valid url if (!GeneralUtils.isUrl(target.getSource())) { throw new PropertyValidationException( - I18nConstantsAnnotation.ANNOTATION_INVALID_TARGET_SOURCE, - I18nConstantsAnnotation.ANNOTATION_INVALID_TARGET_SOURCE, null); + I18nConstantsAnnotation.ANNOTATION_INVALID_URL, + I18nConstantsAnnotation.ANNOTATION_INVALID_URL, new String[] {TARGET_SOURCE}); } } + + private void validateTargetSelectors(Selector selector) throws PropertyValidationException { + if(WebAnnotationFields.RDF_STATEMENT_SELECTOR.equals(selector.getSelectorType())) { + RDFStatementSelector rdfStatSel=(RDFStatementSelector) selector; + if(StringUtils.isBlank(rdfStatSel.getHasPredicate())) { + throw new PropertyValidationException(I18nConstantsAnnotation.MESSAGE_MISSING_MANDATORY_FIELD, + I18nConstantsAnnotation.MESSAGE_MISSING_MANDATORY_FIELD, new String[] {TARGET_SELECTOR_HAS_PREDICATE}); + } + + //the exact field in the refinedBy field is mandatory + if(rdfStatSel.getRefinedBy()!=null) { + if(rdfStatSel.getRefinedBy().getExact()==null) { + throw new PropertyValidationException(I18nConstantsAnnotation.MESSAGE_MISSING_MANDATORY_FIELD, + I18nConstantsAnnotation.MESSAGE_MISSING_MANDATORY_FIELD, new String[] {TARGET_SELECTOR_REFINED_BY_EXACT}); + } + } + + //if hasSubject exists, it must be a URI + if(! StringUtils.isBlank(rdfStatSel.getHasSubject())) { + if(! GeneralUtils.isUrl(rdfStatSel.getHasSubject())) { + throw new PropertyValidationException(I18nConstantsAnnotation.ANNOTATION_INVALID_URL, + I18nConstantsAnnotation.ANNOTATION_INVALID_URL, new String[] {TARGET_SELECTOR_HAS_SUBJECT}); + } + } + } + } private void validateTargetMultipleValues(Target target) throws PropertyValidationException { for (String targetValue : target.getValues()) { diff --git a/annotation-web/src/main/java/eu/europeana/api/common/config/I18nConstantsAnnotation.java b/annotation-web/src/main/java/eu/europeana/api/common/config/I18nConstantsAnnotation.java index 07e06e413..a25d2f97b 100644 --- a/annotation-web/src/main/java/eu/europeana/api/common/config/I18nConstantsAnnotation.java +++ b/annotation-web/src/main/java/eu/europeana/api/common/config/I18nConstantsAnnotation.java @@ -25,7 +25,7 @@ public interface I18nConstantsAnnotation extends eu.europeana.api.commons.defini String INVALID_HEADER_FORMAT = "error.annotation_invalid_format"; String INVALID_PROPERTY_VALUE = "error.annotation_invalid_property_value"; String ANNOTATION_INVALID_TARGET_BASE_URL = "error.annotation_target_base_url"; - String ANNOTATION_INVALID_TARGET_SOURCE = "error.annotation_target_source"; + String ANNOTATION_INVALID_URL = "error.annotation_invalid_url"; String MESSAGE_IDENTIFIER_NOT_NULL = "error.message_identifier_not_null"; String MESSAGE_IDENTIFIER_NULL = "error.message_identifier_null"; diff --git a/annotation-web/src/main/resources/messages_en.properties b/annotation-web/src/main/resources/messages_en.properties index 30245a011..87ea7d76d 100644 --- a/annotation-web/src/main/resources/messages_en.properties +++ b/annotation-web/src/main/resources/messages_en.properties @@ -43,7 +43,7 @@ error.annotation_media_format_not_supported=Media format not supported for mime error.annotation_transcription_validation=Validation error occurred when validating transcription content: {0}. error.annotation_duplication=Found duplicate annotations with the ids: {0} error.annotation_target_base_url=Invalid annotation target base url, which should contain: {0}. -error.annotation_target_source=Invalid annotation target source (must be a url)! +error.annotation_invalid_url=Invalid url in the field: {0} error.message_identifier_not_null=Identifier must not be set when creating a new Annotation for the given provider! {0} error.message_identifier_null=Identifier must not be null for the given provider! {0} From 8b7d29f098472e16e019e4f35f65fe617ff8d91c Mon Sep 17 00:00:00 2001 From: SrdjanStevanetic Date: Sat, 23 Mar 2024 10:56:52 +0100 Subject: [PATCH 2/3] improved annotation_templates.html --- .../src/main/resources/public/annotation_templates.html | 1 + 1 file changed, 1 insertion(+) diff --git a/annotation-web/src/main/resources/public/annotation_templates.html b/annotation-web/src/main/resources/public/annotation_templates.html index f2cf7787b..baed077d0 100644 --- a/annotation-web/src/main/resources/public/annotation_templates.html +++ b/annotation-web/src/main/resources/public/annotation_templates.html @@ -40,6 +40,7 @@

Europeana Annotations - Templates (JSON-LD)

  • Create (Object) tag - web resource
  • Create Link For Contributing - Body As Object
  • Create Link For Contributing - Body As String
  • +
  • Create Debias
  • From 53d536ac4557b36206f60a31108a7eee54730b62 Mon Sep 17 00:00:00 2001 From: srishti Date: Mon, 22 Apr 2024 11:49:48 +0200 Subject: [PATCH 3/3] ## added another cleint configuration constructor with properties --- .../client/config/ClientConfiguration.java | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/annotation-client/src/main/java/eu/europeana/annotation/client/config/ClientConfiguration.java b/annotation-client/src/main/java/eu/europeana/annotation/client/config/ClientConfiguration.java index 3b9d85702..6f2460666 100644 --- a/annotation-client/src/main/java/eu/europeana/annotation/client/config/ClientConfiguration.java +++ b/annotation-client/src/main/java/eu/europeana/annotation/client/config/ClientConfiguration.java @@ -8,18 +8,18 @@ public class ClientConfiguration { protected static final String ANNOTATION_CLIENT_PROPERTIES_FILE = "/annotation-client.properties"; - protected static final String PROP_ANNOTATION_API_KEY = "annotation.api.key"; - protected static final String PROP_ANNOTATION_SERVICE_BASE_URI = "annotation.service.uri"; - protected static final String PROP_ANNOTATION_ID_BASE_URI = "annotation.id.baseUrl"; - protected static final String PROP_ANNOTATION_ITEM_DATA_ENDPOINT = "annotation.item.data.endpoint"; - protected static final String PROP_ANNOTATION_CLIENT_API_ENDPOINT = "annotation.client.api.endpoint"; - - protected static final String PROP_AUTHORIZATION_HEADER_NAME = "annotation.header.name"; - protected static final String PROP_REGULAR_AUTHORIZATION_HEADER_VALUE = "annotation.regular.authorization.value"; - protected static final String PROP_ADMIN_ANNOTATION_HEADER_VALUE = "annotation.admin.authorization.value"; - - protected static final String PROP_OAUTH_SERVICE_URI = "oauth.service.uri"; - protected static final String PROP_OAUTH_REQUEST_PARAMS_PREFIX = "oauth.token.request.params."; + public static final String PROP_ANNOTATION_API_KEY = "annotation.api.key"; + public static final String PROP_ANNOTATION_SERVICE_BASE_URI = "annotation.service.uri"; + public static final String PROP_ANNOTATION_ID_BASE_URI = "annotation.id.baseUrl"; + public static final String PROP_ANNOTATION_ITEM_DATA_ENDPOINT = "annotation.item.data.endpoint"; + public static final String PROP_ANNOTATION_CLIENT_API_ENDPOINT = "annotation.client.api.endpoint"; + + public static final String PROP_AUTHORIZATION_HEADER_NAME = "annotation.header.name"; + public static final String PROP_REGULAR_AUTHORIZATION_HEADER_VALUE = "annotation.regular.authorization.value"; + public static final String PROP_ADMIN_ANNOTATION_HEADER_VALUE = "annotation.admin.authorization.value"; + + public static final String PROP_OAUTH_SERVICE_URI = "oauth.service.uri"; + public static final String PROP_OAUTH_REQUEST_PARAMS_PREFIX = "oauth.token.request.params."; private static Properties properties = null; @@ -44,6 +44,10 @@ public static synchronized ClientConfiguration getInstance() { return singleton; } + public ClientConfiguration(Properties properties) { + this.properties = properties; + } + /** * Laizy loading of configuration properties */