SEBSERV-44 SEBSERV-45 validation on back end
This commit is contained in:
		
							parent
							
								
									8bbf515717
								
							
						
					
					
						commit
						f4af098a6f
					
				
					 7 changed files with 230 additions and 87 deletions
				
			
		|  | @ -9,7 +9,6 @@ | |||
| package ch.ethz.seb.sebserver.gui.service.examconfig.impl; | ||||
| 
 | ||||
| import java.util.Collection; | ||||
| import java.util.function.Consumer; | ||||
| 
 | ||||
| import org.eclipse.rap.rwt.RWT; | ||||
| import org.eclipse.swt.SWT; | ||||
|  | @ -85,59 +84,26 @@ public class TextFieldBuilder implements InputFieldBuilder { | |||
|         errorLabel.setVisible(false); | ||||
|         errorLabel.setData(RWT.CUSTOM_VARIANT, "error"); | ||||
| 
 | ||||
|         addValueChangeListener( | ||||
|                 text, | ||||
|                 attribute, | ||||
|                 orientation, | ||||
|                 viewContext); | ||||
| 
 | ||||
|         return new TextInputField(attribute, orientation, text, errorLabel); | ||||
|     } | ||||
| 
 | ||||
|     private void addValueChangeListener( | ||||
|             final Text control, | ||||
|             final ConfigurationAttribute attribute, | ||||
|             final Orientation orientation, | ||||
|             final ViewContext viewContext) { | ||||
| 
 | ||||
|         final TextInputField textInputField = new TextInputField(attribute, orientation, text, errorLabel); | ||||
|         final ValueChangeListener valueListener = viewContext.getValueChangeListener(); | ||||
|         if (attribute.type == AttributeType.INTEGER) { | ||||
|             addNumberCheckListener(control, attribute, s -> Integer.parseInt(s), viewContext); | ||||
|         } else if (attribute.type == AttributeType.DECIMAL) { | ||||
|             addNumberCheckListener(control, attribute, s -> Double.parseDouble(s), viewContext); | ||||
|         } else { | ||||
|             control.addListener( | ||||
|                     SWT.FocusOut, | ||||
|                     event -> valueListener.valueChanged( | ||||
|         text.addListener( | ||||
|                 SWT.FocusOut, | ||||
|                 event -> { | ||||
|                     textInputField.clearError(); | ||||
|                     valueListener.valueChanged( | ||||
|                             viewContext, | ||||
|                             attribute, | ||||
|                             String.valueOf(control.getText()), | ||||
|                             0)); | ||||
|         } | ||||
|     } | ||||
|                             String.valueOf(text.getText()), | ||||
|                             textInputField.listIndex); | ||||
|                 }); | ||||
| 
 | ||||
|     private void addNumberCheckListener( | ||||
|             final Text control, | ||||
|             final ConfigurationAttribute attribute, | ||||
|             final Consumer<String> numberCheck, | ||||
|             final ViewContext viewContext) { | ||||
| 
 | ||||
|         final ValueChangeListener valueListener = viewContext.getValueChangeListener(); | ||||
|         control.addListener(SWT.FocusOut, event -> { | ||||
|             try { | ||||
|                 final String text = control.getText(); | ||||
|                 numberCheck.accept(text); | ||||
|                 viewContext.clearError(attribute.id); | ||||
|                 valueListener.valueChanged(viewContext, attribute, text, 0); | ||||
|             } catch (final NumberFormatException e) { | ||||
|                 viewContext.showError(attribute.id, "Not A Number"); | ||||
|             } | ||||
|         }); | ||||
|         return textInputField; | ||||
|     } | ||||
| 
 | ||||
|     static final class TextInputField extends ControlFieldAdapter<Text> { | ||||
| 
 | ||||
|         private String initValue = ""; | ||||
|         private int listIndex = 0; | ||||
| 
 | ||||
|         TextInputField( | ||||
|                 final ConfigurationAttribute attribute, | ||||
|  | @ -155,6 +121,7 @@ public class TextFieldBuilder implements InputFieldBuilder { | |||
|                     .findFirst() | ||||
|                     .map(v -> { | ||||
|                         this.initValue = v.value; | ||||
|                         this.listIndex = (v.listIndex != null) ? v.listIndex : 0; | ||||
|                         setDefaultValue(); | ||||
|                         return this.initValue; | ||||
|                     }); | ||||
|  |  | |||
|  | @ -33,6 +33,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue; | |||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; | ||||
| import ch.ethz.seb.sebserver.gbl.util.Result; | ||||
| import ch.ethz.seb.sebserver.gbl.util.Utils; | ||||
| import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationAttributeRecordDynamicSqlSupport; | ||||
| import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationAttributeRecordMapper; | ||||
| import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationRecordMapper; | ||||
|  | @ -156,9 +157,29 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { | |||
|                 .flatMap(this::attributeRecord) | ||||
|                 .map(attributeRecord -> { | ||||
| 
 | ||||
|                     final Long id; | ||||
|                     if (data.id == null) { | ||||
|                         id = this.configurationValueRecordMapper.selectIdsByExample() | ||||
|                                 .where( | ||||
|                                         ConfigurationValueRecordDynamicSqlSupport.configurationId, | ||||
|                                         isEqualTo(data.configurationId)) | ||||
|                                 .and( | ||||
|                                         ConfigurationValueRecordDynamicSqlSupport.configurationAttributeId, | ||||
|                                         isEqualTo(data.attributeId)) | ||||
|                                 .and( | ||||
|                                         ConfigurationValueRecordDynamicSqlSupport.listIndex, | ||||
|                                         isEqualTo(data.listIndex)) | ||||
|                                 .build() | ||||
|                                 .execute() | ||||
|                                 .stream() | ||||
|                                 .collect(Utils.toSingleton()); | ||||
|                     } else { | ||||
|                         id = data.id; | ||||
|                     } | ||||
| 
 | ||||
|                     final boolean bigValue = isBigValue(attributeRecord); | ||||
|                     final ConfigurationValueRecord newRecord = new ConfigurationValueRecord( | ||||
|                             data.id, | ||||
|                             id, | ||||
|                             null, | ||||
|                             null, | ||||
|                             null, | ||||
|  | @ -166,13 +187,8 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { | |||
|                             (bigValue) ? null : data.value, | ||||
|                             (bigValue) ? data.value : null); | ||||
| 
 | ||||
|                     if (data.id != null) { | ||||
|                         this.configurationValueRecordMapper.updateByPrimaryKeySelective(newRecord); | ||||
|                     } else { | ||||
|                         saveByMatch(data, newRecord); | ||||
|                     } | ||||
|                     return this.configurationValueRecordMapper.selectByPrimaryKey(data.id); | ||||
| 
 | ||||
|                     this.configurationValueRecordMapper.updateByPrimaryKeySelective(newRecord); | ||||
|                     return this.configurationValueRecordMapper.selectByPrimaryKey(id); | ||||
|                 }) | ||||
|                 .flatMap(ConfigurationValueDAOImpl::toDomainModel) | ||||
|                 .onError(TransactionHandler::rollback); | ||||
|  | @ -424,29 +440,4 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { | |||
|         return data; | ||||
|     } | ||||
| 
 | ||||
|     /** Try to identify and save attribute value by configurationId and configurationAttributeId and listIndex | ||||
|      * | ||||
|      * @param data | ||||
|      * @param newRecord | ||||
|      * @throws ResourceNotFoundException if no matching attribute value was found */ | ||||
|     private void saveByMatch(final ConfigurationValue data, final ConfigurationValueRecord newRecord) { | ||||
| 
 | ||||
|         final Integer execute = this.configurationValueRecordMapper.updateByExample(newRecord) | ||||
|                 .where( | ||||
|                         ConfigurationValueRecordDynamicSqlSupport.configurationId, | ||||
|                         isEqualTo(data.configurationId)) | ||||
|                 .and( | ||||
|                         ConfigurationValueRecordDynamicSqlSupport.configurationAttributeId, | ||||
|                         isEqualTo(data.attributeId)) | ||||
|                 .and( | ||||
|                         ConfigurationValueRecordDynamicSqlSupport.listIndex, | ||||
|                         isEqualTo(data.listIndex)) | ||||
|                 .build() | ||||
|                 .execute(); | ||||
| 
 | ||||
|         if (execute == null || execute < 0) { | ||||
|             throw new ResourceNotFoundException(EntityType.CONFIGURATION_VALUE, data.toString()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,41 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException; | ||||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; | ||||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; | ||||
| 
 | ||||
| public interface ConfigurationValueValidator { | ||||
| 
 | ||||
|     public static final String MESSAGE_VALUE_OBJECT_NAME = "examConfigValue"; | ||||
| 
 | ||||
|     String name(); | ||||
| 
 | ||||
|     boolean validate( | ||||
|             ConfigurationValue value, | ||||
|             ConfigurationAttribute attribute); | ||||
| 
 | ||||
|     default void throwValidationError( | ||||
|             final ConfigurationValue value, | ||||
|             final ConfigurationAttribute attribute) { | ||||
| 
 | ||||
|         throw new FieldValidationException( | ||||
|                 attribute.name, | ||||
|                 this.createErrorMessage(value, attribute)); | ||||
|     } | ||||
| 
 | ||||
|     default String createErrorMessage( | ||||
|             final ConfigurationValue value, | ||||
|             final ConfigurationAttribute attribute) { | ||||
| 
 | ||||
|         return "examConfigValue:" + attribute.name + ":" + name() + ":" + value.listIndex; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,20 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue; | ||||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; | ||||
| 
 | ||||
| public interface SebExamConfigService { | ||||
| 
 | ||||
|     void validate(ConfigurationValue value); | ||||
| 
 | ||||
|     void validate(ConfigurationTableValue tableValue); | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,55 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; | ||||
| 
 | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; | ||||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; | ||||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator; | ||||
| 
 | ||||
| @Lazy | ||||
| @Component | ||||
| @WebServiceProfile | ||||
| public class IntegerTypeValueValidator implements ConfigurationValueValidator { | ||||
| 
 | ||||
|     @Override | ||||
|     public String name() { | ||||
|         return AttributeType.INTEGER.name(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean validate( | ||||
|             final ConfigurationValue value, | ||||
|             final ConfigurationAttribute attribute) { | ||||
| 
 | ||||
|         // if value is not an integer type or another specific validation is defined --> skip | ||||
|         if (attribute.type != AttributeType.INTEGER || | ||||
|                 StringUtils.isNoneBlank(attribute.validator)) { | ||||
| 
 | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         if (StringUtils.isBlank(value.value)) { | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             Integer.parseInt(value.value); | ||||
|             return true; | ||||
|         } catch (final NumberFormatException nfe) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,61 @@ | |||
| /* | ||||
|  * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) | ||||
|  * | ||||
|  * This Source Code Form is subject to the terms of the Mozilla Public | ||||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; | ||||
| 
 | ||||
| import java.util.Collection; | ||||
| import java.util.Objects; | ||||
| 
 | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Service; | ||||
| 
 | ||||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; | ||||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue; | ||||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; | ||||
| import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigService; | ||||
| 
 | ||||
| @Lazy | ||||
| @Service | ||||
| @WebServiceProfile | ||||
| public class SebExamConfigServiceImpl implements SebExamConfigService { | ||||
| 
 | ||||
|     private final ConfigurationAttributeDAO configurationAttributeDAO; | ||||
|     private final Collection<ConfigurationValueValidator> validators; | ||||
| 
 | ||||
|     protected SebExamConfigServiceImpl( | ||||
|             final ConfigurationAttributeDAO configurationAttributeDAO, | ||||
|             final Collection<ConfigurationValueValidator> validators) { | ||||
| 
 | ||||
|         this.configurationAttributeDAO = configurationAttributeDAO; | ||||
|         this.validators = validators; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void validate(final ConfigurationValue value) { | ||||
|         Objects.requireNonNull(value); | ||||
| 
 | ||||
|         final ConfigurationAttribute attribute = this.configurationAttributeDAO.byPK(value.attributeId) | ||||
|                 .getOrThrow(); | ||||
| 
 | ||||
|         this.validators | ||||
|                 .stream() | ||||
|                 .filter(validator -> !validator.validate(value, attribute)) | ||||
|                 .findFirst() | ||||
|                 .ifPresent(validator -> validator.throwValidationError(value, attribute)); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void validate(final ConfigurationTableValue tableValue) { | ||||
|         // TODO Auto-generated method stub | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -35,6 +35,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionServic | |||
| import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationValueDAO; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigService; | ||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; | ||||
| 
 | ||||
| @WebServiceProfile | ||||
|  | @ -44,6 +45,7 @@ public class ConfigurationValueController extends EntityController<Configuration | |||
| 
 | ||||
|     private final ConfigurationDAO configurationDAO; | ||||
|     private final ConfigurationValueDAO configurationValueDAO; | ||||
|     private final SebExamConfigService sebExamConfigService; | ||||
| 
 | ||||
|     protected ConfigurationValueController( | ||||
|             final AuthorizationService authorization, | ||||
|  | @ -52,7 +54,8 @@ public class ConfigurationValueController extends EntityController<Configuration | |||
|             final UserActivityLogDAO userActivityLogDAO, | ||||
|             final PaginationService paginationService, | ||||
|             final BeanValidationService beanValidationService, | ||||
|             final ConfigurationDAO configurationDAO) { | ||||
|             final ConfigurationDAO configurationDAO, | ||||
|             final SebExamConfigService sebExamConfigService) { | ||||
| 
 | ||||
|         super(authorization, | ||||
|                 bulkActionService, | ||||
|  | @ -63,6 +66,7 @@ public class ConfigurationValueController extends EntityController<Configuration | |||
| 
 | ||||
|         this.configurationDAO = configurationDAO; | ||||
|         this.configurationValueDAO = entityDAO; | ||||
|         this.sebExamConfigService = sebExamConfigService; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|  | @ -138,17 +142,21 @@ public class ConfigurationValueController extends EntityController<Configuration | |||
|                 _entity = entity; | ||||
|             } | ||||
| 
 | ||||
|             // test either id or (configurationId and attributeId and listIndex) are set | ||||
|             if (_entity.id != null || | ||||
|                     (_entity.configurationId != null && | ||||
|                             _entity.attributeId != null && | ||||
|                             _entity.listIndex != null)) { | ||||
|             // ConfigurationValue identity constraint | ||||
|             // test either id or (configurationId and attributeId and listIndex) must be set | ||||
|             final boolean idSet = _entity.id != null; | ||||
|             final boolean idsSet = _entity.configurationId != null && | ||||
|                     _entity.attributeId != null && | ||||
|                     _entity.listIndex != null; | ||||
|             if (!idSet && !idsSet) { | ||||
|                 throw new IllegalAPIArgumentException( | ||||
|                         "Missing some mandatory attributes. Either id must be set or all of configurationId, attributeId and listIndex"); | ||||
| 
 | ||||
|                 return _entity; | ||||
|             } | ||||
| 
 | ||||
|             throw new IllegalAPIArgumentException( | ||||
|                     "Missing some mandatory attributes. Either id must be set or all of configurationId, attributeId and listIndex"); | ||||
|             // apply field type validation | ||||
|             this.sebExamConfigService.validate(_entity); | ||||
|             return _entity; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti