Merge pull request #8175 from the-library-code/TLC-254_type_bind_d72

Type-bind for submission input (port from DSpace-CRIS 7)
This commit is contained in:
Tim Donohue
2022-05-10 10:05:01 -05:00
committed by GitHub
13 changed files with 353 additions and 62 deletions

View File

@@ -561,6 +561,15 @@ public class DCInput {
return true; return true;
} }
/**
* Get the type bind list for use in determining whether
* to display this field in angular dynamic form building
* @return list of bound types
*/
public List<String> getTypeBindList() {
return typeBind;
}
/** /**
* Verify whether the current field contains an entity relationship * Verify whether the current field contains an entity relationship
* This also implies a relationship type is defined for this field * This also implies a relationship type is defined for this field

View File

@@ -140,6 +140,7 @@
<dc-qualifier>ispartofseries</dc-qualifier> <dc-qualifier>ispartofseries</dc-qualifier>
<repeatable>true</repeatable> <repeatable>true</repeatable>
<label>Series/Report No.</label> <label>Series/Report No.</label>
<type-bind>Technical Report</type-bind>
<input-type>series</input-type> <input-type>series</input-type>
<hint>Enter the series and number assigned to this item by your community.</hint> <hint>Enter the series and number assigned to this item by your community.</hint>
<required></required> <required></required>

View File

@@ -155,6 +155,7 @@ public class SubmissionFormConverter implements DSpaceConverter<DCInputSet, Subm
inputField.setInput(inputRest); inputField.setInput(inputRest);
if (dcinput.isMetadataField()) { if (dcinput.isMetadataField()) {
inputField.setSelectableMetadata(selectableMetadata); inputField.setSelectableMetadata(selectableMetadata);
inputField.setTypeBind(dcinput.getTypeBindList());
} }
if (dcinput.isRelationshipField()) { if (dcinput.isRelationshipField()) {
selectableRelationship = getSelectableRelationships(dcinput); selectableRelationship = getSelectableRelationships(dcinput);

View File

@@ -83,6 +83,11 @@ public class SubmissionFormFieldRest {
*/ */
private List<LanguageFormField> languageCodes; private List<LanguageFormField> languageCodes;
/**
* The list of type bind value
*/
private List<String> typeBind;
/** /**
* Getter for {@link #selectableMetadata} * Getter for {@link #selectableMetadata}
* *
@@ -266,6 +271,14 @@ public class SubmissionFormFieldRest {
} }
} }
public List<String> getTypeBind() {
return typeBind;
}
public void setTypeBind(List<String> typeBind) {
this.typeBind = typeBind;
}
public SelectableRelationship getSelectableRelationship() { public SelectableRelationship getSelectableRelationship() {
return selectableRelationship; return selectableRelationship;
} }

View File

@@ -31,6 +31,8 @@ import org.dspace.content.InProgressSubmission;
import org.dspace.content.MetadataValue; import org.dspace.content.MetadataValue;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.core.Utils; import org.dspace.core.Utils;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
/** /**
* Describe step for DSpace Spring Rest. Expose and allow patching of the in progress submission metadata. It is * Describe step for DSpace Spring Rest. Expose and allow patching of the in progress submission metadata. It is
@@ -43,7 +45,11 @@ public class DescribeStep extends AbstractProcessingStep {
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(DescribeStep.class); private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(DescribeStep.class);
// Input reader for form configuration
private DCInputsReader inputReader; private DCInputsReader inputReader;
// Configuration service
private final ConfigurationService configurationService =
DSpaceServicesFactory.getInstance().getConfigurationService();
public DescribeStep() throws DCInputsReaderException { public DescribeStep() throws DCInputsReaderException {
inputReader = new DCInputsReader(); inputReader = new DCInputsReader();
@@ -64,8 +70,17 @@ public class DescribeStep extends AbstractProcessingStep {
private void readField(InProgressSubmission obj, SubmissionStepConfig config, DataDescribe data, private void readField(InProgressSubmission obj, SubmissionStepConfig config, DataDescribe data,
DCInputSet inputConfig) throws DCInputsReaderException { DCInputSet inputConfig) throws DCInputsReaderException {
String documentTypeValue = "";
List<MetadataValue> documentType = itemService.getMetadataByMetadataString(obj.getItem(),
configurationService.getProperty("submit.type-bind.field", "dc.type"));
if (documentType.size() > 0) {
documentTypeValue = documentType.get(0).getValue();
}
for (DCInput[] row : inputConfig.getFields()) { for (DCInput[] row : inputConfig.getFields()) {
for (DCInput input : row) { for (DCInput input : row) {
// Is this input allowed for the document type, as per type bind config? If there is no type
// bind set, this is always true
boolean allowed = input.isAllowedFor(documentTypeValue);
List<String> fieldsName = new ArrayList<String>(); List<String> fieldsName = new ArrayList<String>();
if (input.isQualdropValue()) { if (input.isQualdropValue()) {
@@ -91,6 +106,9 @@ public class DescribeStep extends AbstractProcessingStep {
String[] metadataToCheck = Utils.tokenize(md.getMetadataField().toString()); String[] metadataToCheck = Utils.tokenize(md.getMetadataField().toString());
if (data.getMetadata().containsKey( if (data.getMetadata().containsKey(
Utils.standardize(metadataToCheck[0], metadataToCheck[1], metadataToCheck[2], "."))) { Utils.standardize(metadataToCheck[0], metadataToCheck[1], metadataToCheck[2], "."))) {
// If field is allowed by type bind, add value to existing field set, otherwise remove
// all values for this field
if (allowed) {
data.getMetadata() data.getMetadata()
.get(Utils.standardize(md.getMetadataField().getMetadataSchema().getName(), .get(Utils.standardize(md.getMetadataField().getMetadataSchema().getName(),
md.getMetadataField().getElement(), md.getMetadataField().getElement(),
@@ -98,6 +116,12 @@ public class DescribeStep extends AbstractProcessingStep {
".")) "."))
.add(dto); .add(dto);
} else { } else {
data.getMetadata().remove(Utils.standardize(metadataToCheck[0], metadataToCheck[1],
metadataToCheck[2], "."));
}
} else {
// Add values only if allowed by type bind
if (allowed) {
List<MetadataValueRest> listDto = new ArrayList<>(); List<MetadataValueRest> listDto = new ArrayList<>();
listDto.add(dto); listDto.add(dto);
data.getMetadata() data.getMetadata()
@@ -111,6 +135,7 @@ public class DescribeStep extends AbstractProcessingStep {
} }
} }
} }
}
@Override @Override
public void doPatchProcessing(Context context, HttpServletRequest currentRequest, InProgressSubmission source, public void doPatchProcessing(Context context, HttpServletRequest currentRequest, InProgressSubmission source,

View File

@@ -16,6 +16,7 @@ import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.model.ErrorRest; import org.dspace.app.rest.model.ErrorRest;
import org.dspace.app.rest.repository.WorkspaceItemRestRepository; import org.dspace.app.rest.repository.WorkspaceItemRestRepository;
import org.dspace.app.rest.submit.SubmissionService; import org.dspace.app.rest.submit.SubmissionService;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.util.DCInput; import org.dspace.app.util.DCInput;
import org.dspace.app.util.DCInputSet; import org.dspace.app.util.DCInputSet;
import org.dspace.app.util.DCInputsReader; import org.dspace.app.util.DCInputsReader;
@@ -25,6 +26,7 @@ import org.dspace.content.InProgressSubmission;
import org.dspace.content.MetadataValue; import org.dspace.content.MetadataValue;
import org.dspace.content.authority.service.MetadataAuthorityService; import org.dspace.content.authority.service.MetadataAuthorityService;
import org.dspace.content.service.ItemService; import org.dspace.content.service.ItemService;
import org.dspace.services.ConfigurationService;
/** /**
* Execute three validation check on fields validation: * Execute three validation check on fields validation:
@@ -50,12 +52,20 @@ public class MetadataValidation extends AbstractValidation {
private MetadataAuthorityService metadataAuthorityService; private MetadataAuthorityService metadataAuthorityService;
private ConfigurationService configurationService;
@Override @Override
public List<ErrorRest> validate(SubmissionService submissionService, InProgressSubmission obj, public List<ErrorRest> validate(SubmissionService submissionService, InProgressSubmission obj,
SubmissionStepConfig config) throws DCInputsReaderException, SQLException { SubmissionStepConfig config) throws DCInputsReaderException, SQLException {
List<ErrorRest> errors = new ArrayList<>(); List<ErrorRest> errors = new ArrayList<>();
String documentTypeValue = "";
DCInputSet inputConfig = getInputReader().getInputsByFormName(config.getId()); DCInputSet inputConfig = getInputReader().getInputsByFormName(config.getId());
List<MetadataValue> documentType = itemService.getMetadataByMetadataString(obj.getItem(),
configurationService.getProperty("submit.type-bind.field", "dc.type"));
if (documentType.size() > 0) {
documentTypeValue = documentType.get(0).getValue();
}
for (DCInput[] row : inputConfig.getFields()) { for (DCInput[] row : inputConfig.getFields()) {
for (DCInput input : row) { for (DCInput input : row) {
String fieldKey = String fieldKey =
@@ -71,12 +81,21 @@ public class MetadataValidation extends AbstractValidation {
for (int i = 1; i < inputPairs.size(); i += 2) { for (int i = 1; i < inputPairs.size(); i += 2) {
String fullFieldname = input.getFieldName() + "." + (String) inputPairs.get(i); String fullFieldname = input.getFieldName() + "." + (String) inputPairs.get(i);
List<MetadataValue> mdv = itemService.getMetadataByMetadataString(obj.getItem(), fullFieldname); List<MetadataValue> mdv = itemService.getMetadataByMetadataString(obj.getItem(), fullFieldname);
// If the input is not allowed for this type, strip it from item metadata.
if (!input.isAllowedFor(documentTypeValue)) {
itemService.removeMetadataValues(ContextUtil.obtainCurrentRequestContext(),
obj.getItem(), mdv);
} else {
validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors); validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors);
if (mdv.size() > 0 && input.isVisible(DCInput.SUBMISSION_SCOPE)) { if (mdv.size() > 0 && input.isVisible(DCInput.SUBMISSION_SCOPE)) {
foundResult = true; foundResult = true;
} }
} }
if (input.isRequired() && ! foundResult) { }
// If the input is required but not allowed for this type, and we removed, don't throw
// an error - this way, a field can be required for "Book" to which it is bound, but not
// other types. A user may have switched between types before a final deposit
if (input.isRequired() && !foundResult && input.isAllowedFor(documentTypeValue)) {
// for this required qualdrop no value was found, add to the list of error fields // for this required qualdrop no value was found, add to the list of error fields
addError(errors, ERROR_VALIDATION_REQUIRED, addError(errors, ERROR_VALIDATION_REQUIRED,
"/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId() + "/" + "/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId() + "/" +
@@ -89,6 +108,12 @@ public class MetadataValidation extends AbstractValidation {
for (String fieldName : fieldsName) { for (String fieldName : fieldsName) {
List<MetadataValue> mdv = itemService.getMetadataByMetadataString(obj.getItem(), fieldName); List<MetadataValue> mdv = itemService.getMetadataByMetadataString(obj.getItem(), fieldName);
if (!input.isAllowedFor(documentTypeValue)) {
itemService.removeMetadataValues(ContextUtil.obtainCurrentRequestContext(), obj.getItem(), mdv);
// Continue here, this skips the required check since we've just removed values that previously
// appeared, and the configuration already indicates this field shouldn't be included
continue;
}
validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors); validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors);
if ((input.isRequired() && mdv.size() == 0) && input.isVisible(DCInput.SUBMISSION_SCOPE)) { if ((input.isRequired() && mdv.size() == 0) && input.isVisible(DCInput.SUBMISSION_SCOPE)) {
// since this field is missing add to list of error // since this field is missing add to list of error
@@ -124,6 +149,10 @@ public class MetadataValidation extends AbstractValidation {
} }
} }
public void setConfigurationService(ConfigurationService configurationService) {
this.configurationService = configurationService;
}
public void setItemService(ItemService itemService) { public void setItemService(ItemService itemService) {
this.itemService = itemService; this.itemService = itemService;
} }

View File

@@ -16,6 +16,7 @@
<bean name="metadataValidation" class="org.dspace.app.rest.submit.step.validation.MetadataValidation" <bean name="metadataValidation" class="org.dspace.app.rest.submit.step.validation.MetadataValidation"
scope="prototype"> scope="prototype">
<property name="name" value="submission-form"/> <property name="name" value="submission-form"/>
<property name="configurationService" ref="org.dspace.services.ConfigurationService"/>
<property name="itemService" ref="org.dspace.content.ItemServiceImpl"/> <property name="itemService" ref="org.dspace.content.ItemServiceImpl"/>
<property name="metadataAuthorityService" ref="org.dspace.content.authority.MetadataAuthorityServiceImpl"/> <property name="metadataAuthorityService" ref="org.dspace.content.authority.MetadataAuthorityServiceImpl"/>
</bean> </bean>

View File

@@ -11,6 +11,7 @@ import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.nullValue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@@ -113,20 +114,20 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone"))) .startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone")))
// check the first two rows // check the first two rows
.andExpect(jsonPath("$.rows[0].fields", contains( .andExpect(jsonPath("$.rows[0].fields", contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("name", "Author", SubmissionFormFieldMatcher.matchFormFieldDefinition("name", "Author", null,
null, true,"Add an author", "dc.contributor.author")))) null, true,"Add an author", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains( .andExpect(jsonPath("$.rows[1].fields", contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Title", SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Title", null,
"You must enter a main title for this item.", false, "You must enter a main title for this item.", false,
"Enter the main title of the item.", "dc.title")))) "Enter the main title of the item.", "dc.title"))))
// check a row with multiple fields // check a row with multiple fields
.andExpect(jsonPath("$.rows[3].fields", .andExpect(jsonPath("$.rows[3].fields",
contains( contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("date", "Date of Issue", SubmissionFormFieldMatcher.matchFormFieldDefinition("date", "Date of Issue",
"You must enter at least the year.", false, null, "You must enter at least the year.", false,
"Please give the date", "col-sm-4", "Please give the date", "col-sm-4",
"dc.date.issued"), "dc.date.issued"),
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Publisher", SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Publisher", null,
null, false,"Enter the name of", null, false,"Enter the name of",
"col-sm-8","dc.publisher")))) "col-sm-8","dc.publisher"))))
; ;
@@ -144,18 +145,18 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers .andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone"))) .startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone")))
.andExpect(jsonPath("$.rows[0].fields", contains( .andExpect(jsonPath("$.rows[0].fields", contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("name", "Author", SubmissionFormFieldMatcher.matchFormFieldDefinition("name", "Author", null,
null, true,"Add an author", "dc.contributor.author")))) null, true,"Add an author", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains( .andExpect(jsonPath("$.rows[1].fields", contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Title", SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Title", null,
"You must enter a main title for this item.", false, "You must enter a main title for this item.", false,
"Enter the main title of the item.", "dc.title")))) "Enter the main title of the item.", "dc.title"))))
.andExpect(jsonPath("$.rows[3].fields",contains( .andExpect(jsonPath("$.rows[3].fields",contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("date", "Date of Issue", SubmissionFormFieldMatcher.matchFormFieldDefinition("date", "Date of Issue", null,
"You must enter at least the year.", false, "You must enter at least the year.", false,
"Please give the date", "col-sm-4", "Please give the date", "col-sm-4",
"dc.date.issued"), "dc.date.issued"),
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Publisher", SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Publisher", null,
null, false,"Enter the name of", null, false,"Enter the name of",
"col-sm-8","dc.publisher")))); "col-sm-8","dc.publisher"))));
} }
@@ -220,20 +221,20 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
// dc.subject fields with in separate rows all linked to an authority with different // dc.subject fields with in separate rows all linked to an authority with different
// presentation modes (suggestion, name-lookup, lookup) // presentation modes (suggestion, name-lookup, lookup)
.andExpect(jsonPath("$.rows[0].fields", contains( .andExpect(jsonPath("$.rows[0].fields", contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Author", SubmissionFormFieldMatcher.matchFormFieldDefinition("onebox", "Author", null,
null, true, null, true,
"Author field that can be associated with an authority providing suggestion", "Author field that can be associated with an authority providing suggestion",
null, "dc.contributor.author", "SolrAuthorAuthority") null, "dc.contributor.author", "SolrAuthorAuthority")
))) )))
.andExpect(jsonPath("$.rows[1].fields", contains( .andExpect(jsonPath("$.rows[1].fields", contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("lookup-name", "Editor", SubmissionFormFieldMatcher.matchFormFieldDefinition("lookup-name", "Editor", null,
null, false, null, false,
"Editor field that can be associated with an authority " "Editor field that can be associated with an authority "
+ "providing the special name lookup", + "providing the special name lookup",
null, "dc.contributor.editor", "SolrEditorAuthority") null, "dc.contributor.editor", "SolrEditorAuthority")
))) )))
.andExpect(jsonPath("$.rows[2].fields", contains( .andExpect(jsonPath("$.rows[2].fields", contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("lookup", "Subject", SubmissionFormFieldMatcher.matchFormFieldDefinition("lookup", "Subject", null,
null, true, null, true,
"Subject field that can be associated with an authority providing lookup", "Subject field that can be associated with an authority providing lookup",
null, "dc.subject", "SolrSubjectAuthority") null, "dc.subject", "SolrSubjectAuthority")
@@ -266,7 +267,7 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone"))) .startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone")))
// our test configuration include the dc.type field with a value pair in the 8th row // our test configuration include the dc.type field with a value pair in the 8th row
.andExpect(jsonPath("$.rows[7].fields", contains( .andExpect(jsonPath("$.rows[7].fields", contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("dropdown", "Type", SubmissionFormFieldMatcher.matchFormFieldDefinition("dropdown", "Type", null,
null, true, null, true,
"Select the type(s) of content of the item. To select more than one value in the " + "Select the type(s) of content of the item. To select more than one value in the " +
"list, you may have to hold down the \"CTRL\" or \"Shift\" key.", "list, you may have to hold down the \"CTRL\" or \"Shift\" key.",
@@ -275,6 +276,35 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
; ;
} }
@Test
public void findFieldWithTypeBindConfig() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/config/submissionforms/traditionalpageone"))
// The status has to be 200 OK
.andExpect(status().isOk())
// We expect the content type to be "application/hal+json;charset=UTF-8"
.andExpect(content().contentType(contentType))
// Check that the JSON root matches the expected "traditionalpageone" input forms
.andExpect(jsonPath("$.id", is("traditionalpageone")))
.andExpect(jsonPath("$.name", is("traditionalpageone")))
.andExpect(jsonPath("$.type", is("submissionform")))
.andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/traditionalpageone")))
// check a row with type-bind 'Technical Report'
.andExpect(jsonPath("$.rows[5].fields", contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("series", "Series/Report No.",
"Technical Report", null, true,
"Enter the series and number assigned to this item by your community.",
"dc.relation.ispartofseries"))))
// check the same row with a NON-matching type-bind 'Article' (expect false)
.andExpect(((jsonPath("$.rows[5].fields", not(contains(
SubmissionFormFieldMatcher.matchFormFieldDefinition("series", "Series/Report No.",
"Article", null, true,
"Enter the series and number assigned to this item by your community.",
"dc.relation.ispartofseries")))))));
}
@Test @Test
public void findOpenRelationshipConfig() throws Exception { public void findOpenRelationshipConfig() throws Exception {
String token = getAuthToken(admin.getEmail(), password); String token = getAuthToken(admin.getEmail(), password);
@@ -352,14 +382,15 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers .andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest"))) .startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("name", "Autore", "\u00C8" + " richiesto almeno un autore", true, .matchFormFieldDefinition("name", "Autore", null,
"\u00C8" + " richiesto almeno un autore", true,
"Aggiungi un autore", "dc.contributor.author")))) "Aggiungi un autore", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("onebox", "Titolo", .matchFormFieldDefinition("onebox", "Titolo", null,
"\u00C8" + " necessario inserire un titolo principale per questo item", false, "\u00C8" + " necessario inserire un titolo principale per questo item", false,
"Inserisci titolo principale di questo item", "dc.title")))) "Inserisci titolo principale di questo item", "dc.title"))))
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("dropdown", "Lingua", null, false, .matchFormFieldDefinition("dropdown", "Lingua", null, null, false,
"Selezionare la lingua del contenuto principale dell'item." "Selezionare la lingua del contenuto principale dell'item."
+ " Se la lingua non compare nell'elenco, selezionare (Altro)." + " Se la lingua non compare nell'elenco, selezionare (Altro)."
+ " Se il contenuto non ha davvero una lingua" + " Se il contenuto non ha davvero una lingua"
@@ -376,14 +407,14 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers .andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest"))) .startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("name", "Автор", "Потрібно ввести хочаб одного автора!", .matchFormFieldDefinition("name", "Автор", null, "Потрібно ввести хочаб одного автора!",
true, "Додати автора", "dc.contributor.author")))) true, "Додати автора", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("onebox", "Заголовок", .matchFormFieldDefinition("onebox", "Заголовок", null,
"Заговолок файла обов'язковий !", false, "Заговолок файла обов'язковий !", false,
"Ввести основний заголовок файла", "dc.title")))) "Ввести основний заголовок файла", "dc.title"))))
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("dropdown", "Мова", null, false, .matchFormFieldDefinition("dropdown", "Мова", null, null, false,
"Виберiть мову головного змiсту файлу, як що мови немає у списку, вибрати (Iнша)." "Виберiть мову головного змiсту файлу, як що мови немає у списку, вибрати (Iнша)."
+ " Як що вмiст вайлу не є текстовим, наприклад є фотографiєю, тодi вибрати (N/A)", + " Як що вмiст вайлу не є текстовим, наприклад є фотографiєю, тодi вибрати (N/A)",
null, "dc.language.iso", "common_iso_languages")))); null, "dc.language.iso", "common_iso_languages"))));
@@ -431,14 +462,15 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers .andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest"))) .startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("name", "Autore", "\u00C8" + " richiesto almeno un autore", true, .matchFormFieldDefinition("name", "Autore", null,
"\u00C8" + " richiesto almeno un autore", true,
"Aggiungi un autore", "dc.contributor.author")))) "Aggiungi un autore", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("onebox", "Titolo", .matchFormFieldDefinition("onebox", "Titolo", null,
"\u00C8" + " necessario inserire un titolo principale per questo item", false, "\u00C8" + " necessario inserire un titolo principale per questo item", false,
"Inserisci titolo principale di questo item", "dc.title")))) "Inserisci titolo principale di questo item", "dc.title"))))
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("dropdown", "Lingua", null, false, .matchFormFieldDefinition("dropdown", "Lingua", null, null, false,
"Selezionare la lingua del contenuto principale dell'item." "Selezionare la lingua del contenuto principale dell'item."
+ " Se la lingua non compare nell'elenco, selezionare (Altro)." + " Se la lingua non compare nell'elenco, selezionare (Altro)."
+ " Se il contenuto non ha davvero una lingua" + " Se il contenuto non ha davvero una lingua"
@@ -455,14 +487,14 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers .andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest"))) .startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("name", "Автор", "Потрібно ввести хочаб одного автора!", .matchFormFieldDefinition("name", "Автор", null, "Потрібно ввести хочаб одного автора!",
true, "Додати автора", "dc.contributor.author")))) true, "Додати автора", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("onebox", "Заголовок", .matchFormFieldDefinition("onebox", "Заголовок", null,
"Заговолок файла обов'язковий !", false, "Заговолок файла обов'язковий !", false,
"Ввести основний заголовок файла", "dc.title")))) "Ввести основний заголовок файла", "dc.title"))))
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("dropdown", "Мова", null, false, .matchFormFieldDefinition("dropdown", "Мова", null, null, false,
"Виберiть мову головного змiсту файлу, як що мови немає у списку, вибрати (Iнша)." "Виберiть мову головного змiсту файлу, як що мови немає у списку, вибрати (Iнша)."
+ " Як що вмiст вайлу не є текстовим, наприклад є фотографiєю, тодi вибрати (N/A)", + " Як що вмiст вайлу не є текстовим, наприклад є фотографiєю, тодi вибрати (N/A)",
null, "dc.language.iso", "common_iso_languages")))); null, "dc.language.iso", "common_iso_languages"))));
@@ -505,14 +537,15 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers .andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest"))) .startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("name", "Autore", "\u00C8" + " richiesto almeno un autore", true, .matchFormFieldDefinition("name", "Autore", null,
"\u00C8" + " richiesto almeno un autore", true,
"Aggiungi un autore", "dc.contributor.author")))) "Aggiungi un autore", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("onebox", "Titolo", .matchFormFieldDefinition("onebox", "Titolo", null,
"\u00C8" + " necessario inserire un titolo principale per questo item", false, "\u00C8" + " necessario inserire un titolo principale per questo item", false,
"Inserisci titolo principale di questo item", "dc.title")))) "Inserisci titolo principale di questo item", "dc.title"))))
.andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[2].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("dropdown", "Lingua", null, false, .matchFormFieldDefinition("dropdown", "Lingua", null, null, false,
"Selezionare la lingua del contenuto principale dell'item." "Selezionare la lingua del contenuto principale dell'item."
+ " Se la lingua non compare nell'elenco, selezionare (Altro)." + " Se la lingua non compare nell'elenco, selezionare (Altro)."
+ " Se il contenuto non ha davvero una lingua" + " Se il contenuto non ha davvero una lingua"
@@ -547,10 +580,10 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers .andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest"))) .startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("name", "Autore", "\u00C8 richiesto almeno un autore", true, .matchFormFieldDefinition("name", "Autore", null, "\u00C8 richiesto almeno un autore", true,
"Aggiungi un autore", "dc.contributor.author")))) "Aggiungi un autore", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("onebox", "Titolo", .matchFormFieldDefinition("onebox", "Titolo", null,
"\u00C8 necessario inserire un titolo principale per questo item", false, "\u00C8 necessario inserire un titolo principale per questo item", false,
"Inserisci titolo principale di questo item", "dc.title")))); "Inserisci titolo principale di questo item", "dc.title"))));
resetLocalesConfiguration(); resetLocalesConfiguration();
@@ -582,10 +615,10 @@ public class SubmissionFormsControllerIT extends AbstractControllerIntegrationTe
.andExpect(jsonPath("$._links.self.href", Matchers .andExpect(jsonPath("$._links.self.href", Matchers
.startsWith(REST_SERVER_URL + "config/submissionforms/languagetest"))) .startsWith(REST_SERVER_URL + "config/submissionforms/languagetest")))
.andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[0].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("name", "Autore", "\u00C8 richiesto almeno un autore", true, .matchFormFieldDefinition("name", "Autore", null, "\u00C8 richiesto almeno un autore", true,
"Aggiungi un autore", "dc.contributor.author")))) "Aggiungi un autore", "dc.contributor.author"))))
.andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher .andExpect(jsonPath("$.rows[1].fields", contains(SubmissionFormFieldMatcher
.matchFormFieldDefinition("onebox", "Titolo", .matchFormFieldDefinition("onebox", "Titolo", null,
"\u00C8 necessario inserire un titolo principale per questo item", false, "\u00C8 necessario inserire un titolo principale per questo item", false,
"Inserisci titolo principale di questo item", "dc.title")))); "Inserisci titolo principale di questo item", "dc.title"))));

View File

@@ -1935,6 +1935,141 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
; ;
} }
@Test
/**
* Test the update of metadata for fields configured with type-bind
*
* @throws Exception
*/
public void patchUpdateMetadataWithBindTest() throws Exception {
context.turnOffAuthorisationSystem();
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and two collections.
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
String authToken = getAuthToken(eperson.getEmail(), password);
WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
.withTitle("Workspace Item 1")
.withIssueDate("2017-10-17")
.withSubject("ExtraEntry")
.grantLicense()
.build();
//disable file upload mandatory
configurationService.setProperty("webui.submit.upload.required", false);
context.restoreAuthSystemState();
// Try to add isPartOfSeries (type bound to technical report) - this should not work and instead we'll get
// no JSON path for that field
List<Operation> updateSeries = new ArrayList<Operation>();
List<Map<String, String>> seriesValues = new ArrayList<>();
Map<String, String> value = new HashMap<String, String>();
value.put("value", "New Series");
seriesValues.add(value);
updateSeries.add(new AddOperation("/sections/traditionalpageone/dc.relation.ispartofseries", seriesValues));
String patchBody = getPatchContent(updateSeries);
getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID())
.content(patchBody)
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$",
// Check this - we should match an item with no series or type
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem, null, null))));
// Verify that the metadata isn't in the workspace item
getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$",
// Check this - we should match an item with no series or type
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem, null, null))));
// Set the type to Technical Report confirm it worked
List<Operation> updateType = new ArrayList<>();
List<Map<String, String>> typeValues = new ArrayList<>();
value = new HashMap<String, String>();
value.put("value", "Technical Report");
typeValues.add(value);
updateType.add(new AddOperation("/sections/traditionalpageone/dc.type", typeValues));
patchBody = getPatchContent(updateType);
getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID())
.content(patchBody)
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$",
// Check this - we should now match an item with the expected type and series
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem, "Technical Report",
null))));
getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$",
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem, "Technical Report",
null))));
// Another test, this time adding the series value should be successful and we'll see the value
patchBody = getPatchContent(updateSeries);
getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID())
.content(patchBody)
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$",
// Check this - we should match an item with the expected series and type
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem,
"Technical Report", "New Series"))));
// Verify that the metadata isn't in the workspace item
getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$",
// Check this - we should match an item with the expected series and type
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem,
"Technical Report", "New Series"))));
// One final update, to a different type, this should lose the series as we're back to a non-matching type
updateType = new ArrayList<>();
typeValues = new ArrayList<>();
value = new HashMap<String, String>();
value.put("value", "Article");
typeValues.add(value);
updateType.add(new AddOperation("/sections/traditionalpageone/dc.type", typeValues));
patchBody = getPatchContent(updateType);
getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID())
.content(patchBody)
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$",
// Check this - we should NOT match an item with the series "New Series"
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem, "Article",
null))));
getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$",
Matchers.is(WorkspaceItemMatcher.matchItemWithTypeAndSeries(witem, "Article",
null))));
}
@Test @Test
public void patchUpdateMetadataForbiddenTest() throws Exception { public void patchUpdateMetadataForbiddenTest() throws Exception {
context.turnOffAuthorisationSystem(); context.turnOffAuthorisationSystem();

View File

@@ -10,6 +10,7 @@ package org.dspace.app.rest.matcher;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasNoJsonPath; import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasNoJsonPath;
import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
@@ -28,13 +29,15 @@ public class SubmissionFormFieldMatcher {
/** /**
* Shortcut for the * Shortcut for the
* {@link SubmissionFormFieldMatcher#matchFormFieldDefinition(String, String, String, boolean, String, String, String, String)} * {@link SubmissionFormFieldMatcher#matchFormFieldDefinition(String, String, String, String, boolean, String, String, String, String)}
* with a null style and vocabulary name * with a null style and vocabulary name
* *
* @param type * @param type
* the expected input type * the expected input type
* @param label * @param label
* the expected label * the expected label
* @param typeBind
* the expected type-bind field(s)
* @param mandatoryMessage * @param mandatoryMessage
* the expected mandatoryMessage, can be null. If not empty the fiedl is expected to be flagged as * the expected mandatoryMessage, can be null. If not empty the fiedl is expected to be flagged as
* mandatory * mandatory
@@ -46,21 +49,23 @@ public class SubmissionFormFieldMatcher {
* the expected metadata * the expected metadata
* @return a Matcher for all the condition above * @return a Matcher for all the condition above
*/ */
public static Matcher<? super Object> matchFormFieldDefinition(String type, String label, String mandatoryMessage, public static Matcher<? super Object> matchFormFieldDefinition(String type, String label, String typeBind,
boolean repeatable, String mandatoryMessage, boolean repeatable,
String hints, String metadata) { String hints, String metadata) {
return matchFormFieldDefinition(type, label, mandatoryMessage, repeatable, hints, null, metadata); return matchFormFieldDefinition(type, label, typeBind, mandatoryMessage, repeatable, hints, null, metadata);
} }
/** /**
* Shortcut for the * Shortcut for the
* {@link SubmissionFormFieldMatcher#matchFormFieldDefinition(String, String, String, boolean, String, String, String, String)} * {@link SubmissionFormFieldMatcher#matchFormFieldDefinition(String, String, String, String, boolean, String, String, String, String)}
* with a null controlled vocabulary * with a null controlled vocabulary
* *
* @param type * @param type
* the expected input type * the expected input type
* @param label * @param label
* the expected label * the expected label
* @param typeBind
* the expected type-bind field(s)
* @param mandatoryMessage * @param mandatoryMessage
* the expected mandatoryMessage, can be null. If not empty the field is expected to be flagged as * the expected mandatoryMessage, can be null. If not empty the field is expected to be flagged as
* mandatory * mandatory
@@ -75,10 +80,10 @@ public class SubmissionFormFieldMatcher {
* the expected metadata * the expected metadata
* @return a Matcher for all the condition above * @return a Matcher for all the condition above
*/ */
public static Matcher<? super Object> matchFormFieldDefinition(String type, String label, String mandatoryMessage, public static Matcher<? super Object> matchFormFieldDefinition(String type, String label, String typeBind,
boolean repeatable, String mandatoryMessage, boolean repeatable, String hints, String style, String metadata) {
String hints, String style, String metadata) { return matchFormFieldDefinition(type, label, typeBind, mandatoryMessage, repeatable, hints, style, metadata,
return matchFormFieldDefinition(type, label, mandatoryMessage, repeatable, hints, style, metadata, null); null);
} }
/** /**
@@ -88,6 +93,8 @@ public class SubmissionFormFieldMatcher {
* the expected input type * the expected input type
* @param label * @param label
* the expected label * the expected label
* @param typeBind
* the expected type-bind field(s)
* @param mandatoryMessage * @param mandatoryMessage
* the expected mandatoryMessage, can be null. If not empty the field is expected to be flagged as * the expected mandatoryMessage, can be null. If not empty the field is expected to be flagged as
* mandatory * mandatory
@@ -100,18 +107,20 @@ public class SubmissionFormFieldMatcher {
* missing * missing
* @param metadata * @param metadata
* the expected metadata * the expected metadata
* @param controlled vocabulary * @param controlledVocabulary
* the expected controlled vocabulary, can be null. If null the corresponding json path is expected to be * the expected controlled vocabulary, can be null. If null the corresponding json path is expected to be
* missing * missing
* @return a Matcher for all the condition above * @return a Matcher for all the condition above
*/ */
public static Matcher<? super Object> matchFormFieldDefinition(String type, String label, String mandatoryMessage, public static Matcher<? super Object> matchFormFieldDefinition(String type, String label, String typeBind,
boolean repeatable, String hints, String style, String mandatoryMessage, boolean repeatable,
String metadata, String controlledVocabulary) { String hints, String style, String metadata,
String controlledVocabulary) {
return allOf( return allOf(
// check each field definition // check each field definition
hasJsonPath("$.input.type", is(type)), hasJsonPath("$.input.type", is(type)),
hasJsonPath("$.label", containsString(label)), hasJsonPath("$.label", containsString(label)),
typeBind != null ? hasJsonPath("$.typeBind", contains(typeBind)) : hasNoJsonPath("$.typeBind[0]"),
hasJsonPath("$.selectableMetadata[0].metadata", is(metadata)), hasJsonPath("$.selectableMetadata[0].metadata", is(metadata)),
controlledVocabulary != null ? hasJsonPath("$.selectableMetadata[0].controlledVocabulary", controlledVocabulary != null ? hasJsonPath("$.selectableMetadata[0].controlledVocabulary",
is(controlledVocabulary)) : hasNoJsonPath("$.selectableMetadata[0].controlledVocabulary"), is(controlledVocabulary)) : hasNoJsonPath("$.selectableMetadata[0].controlledVocabulary"),
@@ -166,7 +175,7 @@ public class SubmissionFormFieldMatcher {
hasJsonPath("$.selectableRelationship.filter", is(filter)), hasJsonPath("$.selectableRelationship.filter", is(filter)),
hasJsonPath("$.selectableRelationship.searchConfiguration", is(searchConfiguration)), hasJsonPath("$.selectableRelationship.searchConfiguration", is(searchConfiguration)),
hasJsonPath("$.selectableRelationship.nameVariants", is(String.valueOf(nameVariants))), hasJsonPath("$.selectableRelationship.nameVariants", is(String.valueOf(nameVariants))),
matchFormFieldDefinition(type, label, mandatoryMessage, repeatable, hints, metadata)); matchFormFieldDefinition(type, label, null, mandatoryMessage, repeatable, hints, metadata));
} }
/** /**

View File

@@ -82,6 +82,30 @@ public class WorkspaceItemMatcher {
matchLinks(witem)); matchLinks(witem));
} }
/**
* Check that the workspace item has the expected type and series values
* (used in type bind evaluation)
* @param witem the workspace item
* @param type the dc.type value eg. Technical Report
* @param series the series value eg. 11-23
* @return Matcher result
*/
public static Matcher matchItemWithTypeAndSeries(WorkspaceItem witem, String type, String series) {
return allOf(
// Check workspaceitem properties
matchProperties(witem),
// Check type appears or is null
type != null ?
hasJsonPath("$.sections.traditionalpageone['dc.type'][0].value", is(type)) :
hasNoJsonPath("$.sections.traditionalpageone['dc.type'][0].value"),
// Check series as it appears (for type bind testing)
series != null ?
hasJsonPath("$.sections.traditionalpageone['dc.relation.ispartofseries'][0].value", is(series)) :
hasNoJsonPath("$.sections.traditionalpageone['dc.relation.ispartofseries'][0].value"),
matchLinks(witem)
);
}
/** /**
* Check that the id and type are exposed * Check that the id and type are exposed
* *

View File

@@ -936,6 +936,11 @@ metadata.hide.dc.description.provenance = true
# Defaults to true; If set to 'false', submitter has option to skip upload # Defaults to true; If set to 'false', submitter has option to skip upload
#webui.submit.upload.required = true #webui.submit.upload.required = true
# Which field should be used for type-bind
# Defaults to 'dc.type'; If changing this value, you must also update the related
# dspace-angular environment configuration property submission.typeBind.field
#submit.type-bind.field = dc.type
#### Creative Commons settings ###### #### Creative Commons settings ######
# The url to the web service API # The url to the web service API
@@ -1600,6 +1605,11 @@ request.item.type = all
# Should all Request Copy emails go to the helpdesk instead of the item submitter? # Should all Request Copy emails go to the helpdesk instead of the item submitter?
request.item.helpdesk.override = false request.item.helpdesk.override = false
#------------------------------------------------------------------#
#------------------SUBMISSION CONFIGURATION------------------------#
#------------------------------------------------------------------#
# Field to use for type binding, default dc.type
submit.type-bind.field = dc.type
#------------------------------------------------------------------# #------------------------------------------------------------------#
#-------------------MODULE CONFIGURATIONS--------------------------# #-------------------MODULE CONFIGURATIONS--------------------------#

View File

@@ -33,6 +33,7 @@ rest.projection.specificLevel.maxEmbed = 5
rest.properties.exposed = plugin.named.org.dspace.curate.CurationTask rest.properties.exposed = plugin.named.org.dspace.curate.CurationTask
rest.properties.exposed = google.analytics.key rest.properties.exposed = google.analytics.key
rest.properties.exposed = versioning.item.history.include.submitter rest.properties.exposed = versioning.item.history.include.submitter
rest.properties.exposed = submit.type-bind.field
#---------------------------------------------------------------# #---------------------------------------------------------------#
# These configs are used by the deprecated REST (v4-6) module # # These configs are used by the deprecated REST (v4-6) module #