Skip to content

Commit

Permalink
Complex input field issues (#651)
Browse files Browse the repository at this point in the history
* The complex input field value could be empty - removed condition that requires complex input data to not be empty.

* Fixed issue when the `jsonValEvaluator` is null because of empty operation value.

* Updated doc.

* Mark inputs for funding as required. Added an exception when the openaire_id is not required.

* Updated `submission-forms` complex input field to be not required for Integration tests.

* Refactored and updated validation for complex input field
  • Loading branch information
milanmajchrak authored May 20, 2024
1 parent 95d5f6e commit 6c67510
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -686,17 +686,17 @@ it, please enter the types and the actual numbers or codes.</hint>
Uncomment the example row of the complex input type definition to see this input in the submission UI. -->
<form-complex-definitions>
<definition name="contact_person">
<input name="givenname" input-type="text" label="Given name" required="true"/>
<input name="surname" input-type="text" label="Surname" required="true"/>
<input name="email" input-type="text" label="Email" regex="[^@]+@[^\.@]+\.[^@]+" required="true"/>
<input name="givenname" input-type="text" label="Given name"/>
<input name="surname" input-type="text" label="Surname"/>
<input name="email" input-type="text" label="Email" regex="[^@]+@[^\.@]+\.[^@]+"/>
<input name="affiliation" input-type="text" label="Affiliation"/>
</definition>

<definition name="funding">
<input name="type" input-type="dropdown" label="Funding type" required="true" value-pairs-name="metashare_funding"/>
<input name="code" input-type="autocomplete" label="Grant no. or funding project code" required="true"/>
<input name="orgname" input-type="text" label="Funding organization" required="true"/>
<input name="projname" input-type="autocomplete" label="Funding project name" required="true"/>
<input name="type" input-type="dropdown" label="Funding type" value-pairs-name="metashare_funding"/>
<input name="code" input-type="autocomplete" label="Grant no. or funding project code"/>
<input name="orgname" input-type="text" label="Funding organization"/>
<input name="projname" input-type="autocomplete" label="Funding project name"/>
<input name="openaire_id" input-type="text" label="EU Project Identifier (OpenAIRE)" readonly="true" placeholder="Filled out automatically..." mapped-to-if-not-default="dc.relation"/>
</definition>
</form-complex-definitions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,6 @@ private Operation getOperationWithChangedMetadataField(Operation oldOp, String m
String[] opPathArray = oldOpPathArray.clone();

// metadata field could has more metadata values
// TO DO problem je v tom, ze sa volala replace metoda pri add
// potrebujem zvlast odchytit ADD metodu a REPLACE
boolean isNotFirstValue = false;
boolean removeMetadata = false;

Expand All @@ -262,6 +260,10 @@ private Operation getOperationWithChangedMetadataField(Operation oldOp, String m

// Operation has a value wrapped in the JsonValueEvaluator
JsonValueEvaluator jsonValEvaluator = (JsonValueEvaluator) oldOp.getValue();
if (jsonValEvaluator == null) {
log.warn("The complex input field is not processed, because the operation doesn't have a value.");
return null;
}
Iterator<JsonNode> jsonNodes = jsonValEvaluator.getValueNode().elements();

if (isNotFirstValue) {
Expand All @@ -277,10 +279,6 @@ private Operation getOperationWithChangedMetadataField(Operation oldOp, String m
}
}

if (ObjectUtils.isEmpty(jsonNodeValue) || StringUtils.isBlank(jsonNodeValue.asText())) {
throw new UnprocessableEntityException("Cannot load JsonNode value from the operation: " +
oldOp.getPath());
}
// get the value from the old operation as a string
oldOpValue = jsonNodeValue.asText();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.List;
import java.util.Map;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
Expand Down Expand Up @@ -142,8 +143,9 @@ public List<ErrorRest> validate(SubmissionService submissionService, InProgressS
}
}
validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors);
if ((input.isRequired() && mdv.size() == 0) && input.isVisible(DCInput.SUBMISSION_SCOPE)
&& !valuesRemoved) {
if (((input.isRequired() && mdv.size() == 0) && input.isVisible(DCInput.SUBMISSION_SCOPE)
&& !valuesRemoved)
|| !isValidComplexDefinitionMetadata(input, mdv)) {
// Is the input required for *this* type? In other words, are we looking at a required
// input that is also allowed for this document type
if (input.isAllowedFor(documentTypeValue)) {
Expand All @@ -169,26 +171,76 @@ public List<ErrorRest> validate(SubmissionService submissionService, InProgressS
return errors;
}

/**
* Check if the metadata values for a complex definition input are valid.
* Valid if:
* - the complex input field is required and all required nested input fields are filled in.
* - the complex input field is not required, if there is a valued in the nested input field - all required nested
* input fields must be filled in.
* - the complex input field is not required, and none of the nested input fields are required.
*/
private boolean isValidComplexDefinitionMetadata(DCInput input, List<MetadataValue> mdv) {
if (input.getInputType().equals("complex")) {
int complexDefinitionIndex = 0;
Map<String, Map<String, String>> complexDefinitionInputs = input.getComplexDefinition().getInputs();
for (String complexDefinitionInputName : complexDefinitionInputs.keySet()) {
Map<String, String> complexDefinitionInputValues =
complexDefinitionInputs.get(complexDefinitionInputName);

List<String> filledInputValues = null;
String isRequired = complexDefinitionInputValues.get("required");
if (StringUtils.equals(BooleanUtils.toStringTrueFalse(true), isRequired)) {
filledInputValues = new ArrayList<>(Arrays.asList(
mdv.get(0).getValue().split(DCInput.ComplexDefinitions.getSeparator(),-1)));

if (StringUtils.isBlank(filledInputValues.get(complexDefinitionIndex))) {
return false;
}
}
complexDefinitionIndex++;
// The input is not a complex definition - do not validate it
if (!input.getInputType().equals("complex")) {
return true;
}

// Get the complex definition nested inputs
Map<String, Map<String, String>> complexDefinitionInputs = input.getComplexDefinition().getInputs();

// Check valid state of the complex definition input when it is required
if (input.isRequired()) {
// There are no values in the complex input field
if (CollectionUtils.isEmpty(mdv)) {
return false;
}
} else {
// The complex input field is not required
if (CollectionUtils.isEmpty(mdv)) {
// There are no values in the complex input field
return true;
}
}
return checkAllRequiredInputFieldsAreFilledIn(complexDefinitionInputs, mdv);
}

/**
* Check if all required nested input fields are filled in.
*/
private boolean checkAllRequiredInputFieldsAreFilledIn(Map<String, Map<String, String>> complexDefinitionInputs,
List<MetadataValue> mdv) {
// If any of the nested input fields are filled in - all required nested input fields must be filled in
int complexDefinitionIndex = -1;
// Go through all nested input fields
for (String complexDefinitionInputName : complexDefinitionInputs.keySet()) {
complexDefinitionIndex++;

// Get the definition of the nested input field
Map<String, String> complexDefinitionInputValues =
complexDefinitionInputs.get(complexDefinitionInputName);
// Check if the nested input field is required - if not do not check if it is filled in
if (!StringUtils.equals(BooleanUtils.toStringTrueFalse(true),
complexDefinitionInputValues.get("required"))) {
continue;
}

// Load filled in values of the nested input field
List<String> filledInputValues = new ArrayList<>(Arrays.asList(
mdv.get(0).getValue().split(DCInput.ComplexDefinitions.getSeparator(),-1)));

// Check if the required nested input field is filled in. It is valid if there is a value in the nested
// input.
if (!StringUtils.isBlank(filledInputValues.get(complexDefinitionIndex))) {
continue;
}

// EU identifier must have `openaire_id` value otherwise the `openaire_id` could be empty.
if (StringUtils.equals("openaire_id", complexDefinitionInputName) &&
!StringUtils.equals("euFunds", filledInputValues.get(0))) {
continue;
}
return false;

}
return true;
}
Expand Down
10 changes: 5 additions & 5 deletions dspace/config/submission-forms.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3178,11 +3178,11 @@
</definition>

<definition name="funding">
<input name="type" input-type="dropdown" label="Funding type" value-pairs-name="metashare_funding"/>
<input name="code" input-type="autocomplete" label="Grant no. or funding project code"/>
<input name="orgname" input-type="text" label="Funding organization"/>
<input name="projname" input-type="autocomplete" label="Funding project name"/>
<input name="openaire_id" input-type="text" label="EU Project Identifier (OpenAIRE)" readonly="true" placeholder="Filled out automatically..." mapped-to-if-not-default="dc.relation"/>
<input name="type" input-type="dropdown" label="Funding type" value-pairs-name="metashare_funding" required="true"/>
<input name="code" input-type="autocomplete" label="Grant no. or funding project code" required="true"/>
<input name="orgname" input-type="text" label="Funding organization" required="true"/>
<input name="projname" input-type="autocomplete" label="Funding project name" required="true"/>
<input name="openaire_id" input-type="text" label="EU Project Identifier (OpenAIRE)" readonly="true" placeholder="Filled out automatically..." mapped-to-if-not-default="dc.relation" required="true"/>
</definition>

<definition name="sizeInfo">
Expand Down

0 comments on commit 6c67510

Please sign in to comment.