diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java index 37d2a4aac82..8996a970427 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java @@ -791,21 +791,21 @@ public FeatureResponse getFeatureCatalog( @io.swagger.v3.oas.annotations.Operation(summary = "Check if metadata title is duplicated", description = "Verifies if the metadata title is in use.") - @PostMapping(value = "/{metadataUuid:.+}/checkDuplicatedTitle", + @PostMapping(value = "/{metadataUuid:.+}/checkDuplicatedFieldValue", produces = {MediaType.APPLICATION_JSON_VALUE}) @PreAuthorize("hasAuthority('Editor')") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Return true if the title is duplicated or false in other case."), @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_VIEW) }) - public ResponseEntity checkMetadataTitleDuplicated( + public ResponseEntity checkDuplicatedFieldValue( @Parameter(description = API_PARAM_RECORD_UUID, required = true) @PathVariable String metadataUuid, - @Parameter(description = "Metadata title to check", + @Parameter(description = "Metadata field information to check", required = true) - @RequestBody String title, + @RequestBody DuplicatedValueDto duplicatedValueDto, HttpServletRequest request ) throws Exception { try { @@ -815,7 +815,18 @@ public ResponseEntity checkMetadataTitleDuplicated( throw new NotAllowedException(ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_VIEW); } - boolean uuidsWithSameTitle = isMetadataTitleExistingInOtherRecords(title, metadataUuid); + List validFields = Arrays.asList("title", "altTitle", "identifier"); + + if (!validFields.contains(duplicatedValueDto.getField())) { + throw new IllegalArgumentException(String.format("A valid Lucene field name is required:", String.join(",", validFields))); + } + + if (StringUtils.isEmpty(duplicatedValueDto.getValue())) { + throw new IllegalArgumentException("A non-empty value is required."); + } + + + boolean uuidsWithSameTitle = MetadataUtils.isMetadataFieldValueExistingInOtherRecords(duplicatedValueDto.getValue(), duplicatedValueDto.getField(), metadataUuid); return ResponseEntity.ok(uuidsWithSameTitle); } @@ -828,36 +839,24 @@ private boolean isIncludedAttributeTable(RelatedResponse.Fcat fcat) { && fcat.getItem().get(0).getFeatureType().getAttributeTable().getElement() != null; } + private static class DuplicatedValueDto { + private String field; + private String value; - /** - * Check if other metadata records exist apart from the one with {code}metadataUuidToExclude{code} with - * {code}metadataTitle{code} title in the catalogue. - * - * @param metadataTitle Metadata title to check. - * @param metadataUuidToExclude Metadata identifier to exclude from the search. - * @return A list of metadata uuids that have the same metadata title. - */ - private boolean isMetadataTitleExistingInOtherRecords(String metadataTitle, String metadataUuidToExclude) { - boolean metadataWithSameTitle = false; - String jsonQuery = " {" + - " \"query_string\": {" + - " \"query\": \"+resourceTitleObject.\\\\*.keyword:\\\"%s\\\" -uuid:\\\"%s\\\"\"" + - " }" + - "}"; - - ObjectMapper objectMapper = new ObjectMapper(); - try { - JsonNode esJsonQuery = objectMapper.readTree(String.format(jsonQuery, metadataTitle, metadataUuidToExclude)); + public String getField() { + return field; + } - final SearchResponse queryResult = esSearchManager.query( - esJsonQuery, - FIELDLIST_UUID, - 0, 5); + public void setField(String field) { + this.field = field; + } + + public String getValue() { + return value; + } - metadataWithSameTitle = !queryResult.hits().hits().isEmpty(); - } catch (Exception ex) { - Log.error(API.LOG_MODULE_NAME, ex.getMessage(), ex); + public void setValue(String value) { + this.value = value; } - return metadataWithSameTitle; } } diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataUtils.java b/services/src/main/java/org/fao/geonet/api/records/MetadataUtils.java index 76bb7f594f3..91eeb3c7ff4 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataUtils.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataUtils.java @@ -37,6 +37,7 @@ import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.GeonetContext; import org.fao.geonet.NodeInfo; +import org.fao.geonet.api.API; import org.fao.geonet.api.es.EsHTTPProxy; import org.fao.geonet.api.records.model.related.AssociatedRecord; import org.fao.geonet.api.records.model.related.RelatedItemOrigin; @@ -306,7 +307,7 @@ public static Map> getAssociated( if (!e.fields().isEmpty()) { FIELDLIST_RELATED_SCRIPTED.keySet().forEach(f -> { JsonData dc = (JsonData) e.fields().get(f); - + if (dc != null) { if (associatedRecord.getProperties() == null) { associatedRecord.setProperties(new HashMap<>()); @@ -774,6 +775,48 @@ public static boolean retrieveMetadataValidationStatus(AbstractMetadata metadata return isInvalid; } + /** + * Check if other metadata records exist apart from the one with {code}metadataUuidToExclude{code} with the same + * {code}metadataValue{code} for the field {code}metadataField{code}. + * + * @param metadataValue Metadata value to check. + * @param metadataField Metadata field to check the value. + * @param metadataUuidToExclude Metadata identifier to exclude from the search. + * @return A list of metadata uuids that have the same value for the field provided. + */ + public static boolean isMetadataFieldValueExistingInOtherRecords(String metadataValue, String metadataField, String metadataUuidToExclude) { + ApplicationContext applicationContext = ApplicationContextHolder.get(); + EsSearchManager searchMan = applicationContext.getBean(EsSearchManager.class); + + String esFieldName = "resourceTitleObject.\\\\*.keyword"; + if (metadataField.equals("altTitle")) { + esFieldName = "resourceAltTitleObject.\\\\*.keyword"; + } else if (metadataField.equals("identifier")) { + esFieldName = "resourceIdentifier.code"; + } + + boolean duplicatedMetadataValue = false; + String jsonQuery = " {" + + " \"query_string\": {" + + " \"query\": \"+" + esFieldName + ":\\\"%s\\\" -uuid:\\\"%s\\\"\"" + + " }" + + "}"; + + ObjectMapper objectMapper = new ObjectMapper(); + try { + JsonNode esJsonQuery = objectMapper.readTree(String.format(jsonQuery, metadataValue, metadataUuidToExclude)); + + final SearchResponse queryResult = searchMan.query( + esJsonQuery, + FIELDLIST_UUID, + 0, 5); + + duplicatedMetadataValue = !queryResult.hits().hits().isEmpty(); + } catch (Exception ex) { + Log.error(API.LOG_MODULE_NAME, ex.getMessage(), ex); + } + return duplicatedMetadataValue; + } /** * Checks if a result for a search query has results. diff --git a/web-ui/src/main/resources/catalog/components/edit/FieldsDirective.js b/web-ui/src/main/resources/catalog/components/edit/FieldsDirective.js index f8b146c386d..1e4dc4c90ed 100644 --- a/web-ui/src/main/resources/catalog/components/edit/FieldsDirective.js +++ b/web-ui/src/main/resources/catalog/components/edit/FieldsDirective.js @@ -545,43 +545,60 @@ /** * @ngdoc directive - * @name gn_fields.directive:gnDuplicatedMetadataTitleChecker + * @name gn_fields.directive:gnDuplicatedMetadataValueChecker * * @description - * Checks if the associated control value exists in another metadata record title. + * Checks if the associated control value exists in another metadata record. + * Valid field keys: + * - title: Metadata title. + * - altTitle: Metadata alternative title. + * - identifier: Metadata resource identifier. * Configure in your metadata schema config-editor.xml the usage of this directive * for the title element. For example, for iso19139: * * ... - * + * + * */ - module.directive("gnDuplicatedMetadataTitleChecker", [ + module.directive("gnDuplicatedMetadataValueChecker", [ "gnCurrentEdit", "$http", "$compile", - function (gnCurrentEdit, $http, $compile) { + "$translate", + function (gnCurrentEdit, $http, $compile, $translate) { return { restrict: "A", - scope: {}, + scope: { + fieldLabel: "@", // Field name used to match a translation message with the key: 'metadataDuplicatedField' + scope.fieldName + fieldKey: "@" // Elasticserch field name. Allowed values: _title (Metadata title), altTitle (Resource name), identifier (Resource identifier) + }, link: function (scope, element, attrs) { + var fieldName = $translate.instant( + "metadataDuplicatedField" + scope.fieldLabel + ); + var messageTemplate = "

metadataDuplicatedTitle

"; + "data-ng-show='duplicatedValue && !hiddenControl' " + + "data-translate>" + + fieldName + + "

"; var messageTemplateCompiled = $compile(messageTemplate)(scope); - var messageTarget = element.context; + var messageTarget = document.getElementById(element[0].id); element.blur(function () { - if (messageTarget.value !== scope.metadataTitle) { - scope.metadataTitle = messageTarget.value; - scope.checkTitle(scope.metadataTitle, scope.metadataUuid); + if (messageTarget.value !== scope.metadataFieldValue) { + scope.metadataFieldValue = messageTarget.value; + scope.checkField(scope.metadataFieldValue, scope.metadataUuid); } }); scope.metadataUuid = gnCurrentEdit.uuid; - scope.metadataTitle = messageTarget.value; - scope.duplicatedTitle = false; + scope.metadataFieldValue = messageTarget.value; + scope.duplicatedValue = false; scope.hiddenControl = false; element.after(messageTemplateCompiled); @@ -602,18 +619,28 @@ characterData: false }); - scope.checkTitle = function (metadataTitle, metadataUuid) { + scope.checkField = function (fieldValue, metadataUuid) { + if (fieldValue === "") { + scope.duplicatedValue = false; + return; + } + + var postBody = { + field: scope.fieldKey, + value: fieldValue + }; + $http .post( - "../api/records/" + metadataUuid + "/checkDuplicatedTitle", - metadataTitle + "../api/records/" + metadataUuid + "/checkDuplicatedFieldValue", + postBody ) .then(function (response) { - scope.duplicatedTitle = response.data === true; + scope.duplicatedValue = response.data === true; }); }; - scope.checkTitle(scope.metadataTitle, scope.metadataUuid); + scope.checkField(scope.metadataFieldValue, scope.metadataUuid); } }; } diff --git a/web-ui/src/main/resources/catalog/locales/en-editor.json b/web-ui/src/main/resources/catalog/locales/en-editor.json index c22d92c6e5a..a2e2ba082c7 100644 --- a/web-ui/src/main/resources/catalog/locales/en-editor.json +++ b/web-ui/src/main/resources/catalog/locales/en-editor.json @@ -459,6 +459,5 @@ "associated-hasfeaturecats": "Using this feature catalog", "associatedResourcesPanel": "Associated resources", "validationSuccessLabel": "success", - "validationErrorLabel": "errors", - "metadataDuplicatedTitle": "The title is used in another metadata record." + "validationErrorLabel": "errors" }