From 8867721a8a13353accd64c522773bd28fc3a8ea2 Mon Sep 17 00:00:00 2001 From: anhefti Date: Fri, 10 May 2019 14:09:56 +0200 Subject: [PATCH] SEBSERV-44 SEBSERV-45 exam config table implementation --- pom.xml | 3 +- .../ch/ethz/seb/sebserver/gbl/api/API.java | 2 + .../seb/sebserver/gbl/api/APIMessage.java | 26 +++ .../gbl/async/MemoizingCircuitBreaker.java | 2 + .../gbl/model/sebconfig/AttributeType.java | 2 +- ...lue.java => ConfigurationTableValues.java} | 6 +- .../seb/sebserver/gui/form/FormBuilder.java | 12 +- .../gui/service/examconfig/InputField.java | 4 + .../service/examconfig/InputFieldBuilder.java | 13 ++ .../examconfig/ValueChangeListener.java | 4 +- .../examconfig/impl/AbstractInputField.java | 14 +- .../examconfig/impl/CheckBoxBuilder.java | 10 +- .../impl/ExamConfigurationServiceImpl.java | 25 +-- .../impl/InputFieldBuilderSupplier.java | 47 +++++ .../service/examconfig/impl/LabelBuilder.java | 7 +- .../examconfig/impl/PassworFieldBuilder.java | 42 ++-- .../service/examconfig/impl/TableContext.java | 186 ++++++++++++++++++ .../impl/{table => }/TableFieldBuilder.java | 185 ++++++++--------- .../examconfig/impl/TableRowFormBuilder.java | 124 ++++++++++++ .../examconfig/impl/TextFieldBuilder.java | 7 +- .../service/examconfig/impl/ViewContext.java | 55 +++++- .../examconfig/impl/ViewGridBuilder.java | 21 +- .../impl/table/TableRowFormBuilder.java | 36 ---- .../service/i18n/impl/I18nSupportImpl.java | 11 +- .../service/page/impl/ModalInputDialog.java | 8 + .../GetExamConfigTableRowValues.java | 42 ++++ ...ue.java => SaveExamConfigTableValues.java} | 8 +- .../sebserver/gui/widget/WidgetFactory.java | 13 ++ .../PermissionDeniedException.java | 2 + .../authorization/SEBServerUser.java | 5 +- .../bulkaction/BulkActionSupportDAO.java | 2 +- .../dao/ConfigurationValueDAO.java | 16 +- .../servicelayer/dao/DAOLoggingSupport.java | 2 +- .../dao/ResourceNotFoundException.java | 1 + .../impl/ConfigurationAttributeDAOImpl.java | 4 +- .../dao/impl/ConfigurationDAOImpl.java | 4 +- .../dao/impl/ConfigurationNodeDAOImpl.java | 11 +- .../dao/impl/ConfigurationValueDAOImpl.java | 154 ++++++++------- .../dao/impl/ExamConfigurationMapDAOImpl.java | 4 +- .../dao/impl/IndicatorDAOImpl.java | 6 +- .../dao/impl/InstitutionDAOImpl.java | 6 +- .../dao/impl/LmsSetupDAOImpl.java | 6 +- .../dao/impl/OrientationDAOImpl.java | 4 +- .../dao/impl/SebClientConfigDAOImpl.java | 6 +- .../dao/impl/UserActivityLogDAOImpl.java | 6 +- .../servicelayer/dao/impl/UserDAOImpl.java | 6 +- .../servicelayer/dao/impl/ViewDAOImpl.java | 4 +- .../sebconfig/SebExamConfigService.java | 4 +- .../impl/SebExamConfigServiceImpl.java | 14 +- ...a => APIConstraintViolationException.java} | 10 +- .../weblayer/api/APIExceptionHandler.java | 4 +- .../api/ConfigurationValueController.java | 43 +++- .../weblayer/api/EntityController.java | 4 +- .../api/SebClientConfigController.java | 2 +- .../weblayer/api/UserAccountController.java | 6 +- src/main/resources/NewFile.xml | 32 --- 56 files changed, 906 insertions(+), 377 deletions(-) rename src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/{ConfigurationTableValue.java => ConfigurationTableValues.java} (91%) create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/InputFieldBuilderSupplier.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableContext.java rename src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/{table => }/TableFieldBuilder.java (70%) create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableRowFormBuilder.java delete mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/table/TableRowFormBuilder.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/GetExamConfigTableRowValues.java rename src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/{SaveExamConfigTableValue.java => SaveExamConfigTableValues.java} (83%) rename src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/{IllegalAPIArgumentException.java => APIConstraintViolationException.java} (51%) delete mode 100644 src/main/resources/NewFile.xml diff --git a/pom.xml b/pom.xml index ccd1c323..a3280ec5 100644 --- a/pom.xml +++ b/pom.xml @@ -111,7 +111,8 @@ false **/batis/mapper/*.java - **/batis/model/*.java + **/batis/model/*.java + diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java index be507659..f72ddeb6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java @@ -71,6 +71,8 @@ public final class API { public static final String CONFIGURATION_VALUE_ENDPOINT = "/configuration_value"; public static final String CONFIGURATION_TABLE_VALUE_PATH_SEGMENT = "/table"; + public static final String CONFIGURATION_TABLE_ROW_VALUE_PATH_SEGMENT = + CONFIGURATION_TABLE_VALUE_PATH_SEGMENT + "/row"; public static final String CONFIGURATION_ATTRIBUTE_ENDPOINT = "/configuration_attribute"; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessage.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessage.java index e93f3785..1a47c15a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessage.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessage.java @@ -24,11 +24,17 @@ import com.fasterxml.jackson.annotation.JsonProperty; import ch.ethz.seb.sebserver.gbl.util.Utils; +/** This class defines API error messages that are created and responded on error and/or exceptional + * cases within the web-service. */ public class APIMessage implements Serializable { private static final long serialVersionUID = -6858683658311637361L; + /** An enumeration of error messages defining the error code, the HTTP status for the response + * and a short system message. This error message definition can be used to + * generate APIMessages for default errors. */ public enum ErrorMessage { + /** For every unknown or unspecific internal error */ GENERIC("0", HttpStatus.INTERNAL_SERVER_ERROR, "Generic error message"), UNAUTHORIZED("1000", HttpStatus.UNAUTHORIZED, "UNAUTHORIZED"), FORBIDDEN("1001", HttpStatus.FORBIDDEN, "FORBIDDEN"), @@ -89,12 +95,19 @@ public class APIMessage implements Serializable { } } + /** A specific message code that can be used to identify the type of message */ @JsonProperty("messageCode") public final String messageCode; + + /** A short system message that describes the cause */ @JsonProperty("systemMessage") public final String systemMessage; + + /** Message details */ @JsonProperty("details") public final String details; + + /** A list of additional attributes */ @JsonProperty("attributes") public final List attributes; @@ -137,6 +150,11 @@ public class APIMessage implements Serializable { return this.attributes; } + /** Use this as a conversion from a given FieldError of Spring to a APIMessage + * of type field validation. + * + * @param error FieldError instance + * @return converted APIMessage of type field validation */ public static final APIMessage fieldValidationError(final FieldError error) { final String[] args = StringUtils.split(error.getDefaultMessage(), ":"); return ErrorMessage.FIELD_VALIDATION.of(error.toString(), args); @@ -165,6 +183,10 @@ public class APIMessage implements Serializable { return builder.toString(); } + /** This exception can be internal used to wrap a created APIMessage + * within an Exception and throw. The Exception will be caught a the + * APIExceptionHandler endpoint. The APIMessage will be extracted + * and send as response. */ public static class APIMessageException extends RuntimeException { private static final long serialVersionUID = 1453431210820677296L; @@ -196,6 +218,10 @@ public class APIMessage implements Serializable { } } + /** This is used as a field validation exception that creates a APIMessage of filed + * validation. The Exception will be caught a the + * APIExceptionHandler endpoint. The APIMessage will be extracted + * and send as response. */ public static class FieldValidationException extends RuntimeException { private static final long serialVersionUID = 3324566460573096815L; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreaker.java b/src/main/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreaker.java index 340c430c..c7be11a6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreaker.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreaker.java @@ -46,6 +46,8 @@ import ch.ethz.seb.sebserver.gbl.util.Result; * @param The of the result of the suppling function */ public final class MemoizingCircuitBreaker implements Supplier> { + // TODO considering invalidation time for memoizing + private static final Logger log = LoggerFactory.getLogger(MemoizingCircuitBreaker.class); private final CircuitBreaker delegate; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/AttributeType.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/AttributeType.java index c3d902a7..f3edb939 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/AttributeType.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/AttributeType.java @@ -39,7 +39,7 @@ public enum AttributeType { FILE_UPLOAD(BASE64_BINARY), - /** Table type is a list of composite */ + /** Table type is a list of a composite of single types */ TABLE(COMPOSITE_LIST), ; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ConfigurationTableValue.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ConfigurationTableValues.java similarity index 91% rename from src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ConfigurationTableValue.java rename to src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ConfigurationTableValues.java index 54146fd1..297a967d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ConfigurationTableValue.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ConfigurationTableValues.java @@ -22,7 +22,7 @@ import ch.ethz.seb.sebserver.gbl.model.Domain.CONFIGURATION_VALUE; import ch.ethz.seb.sebserver.gbl.model.GrantEntity; @JsonIgnoreProperties(ignoreUnknown = true) -public final class ConfigurationTableValue implements GrantEntity { +public final class ConfigurationTableValues implements GrantEntity { public static final String ATTR_TABLE_VALUES = "tableValues"; @@ -42,7 +42,7 @@ public final class ConfigurationTableValue implements GrantEntity { public final List values; @JsonCreator - public ConfigurationTableValue( + public ConfigurationTableValues( @JsonProperty(CONFIGURATION_VALUE.ATTR_INSTITUTION_ID) final Long institutionId, @JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ID) final Long configurationId, @JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID) final Long attributeId, @@ -88,7 +88,7 @@ public final class ConfigurationTableValue implements GrantEntity { @Override public String toString() { - return "ConfigurationTableValue [institutionId=" + this.institutionId + ", configurationId=" + return "ConfigurationTableValues [institutionId=" + this.institutionId + ", configurationId=" + this.configurationId + ", attributeId=" + this.attributeId + ", values=" + this.values + "]"; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java index e932d3f2..143a7835 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java @@ -17,7 +17,6 @@ import org.apache.commons.lang3.StringUtils; import org.eclipse.rap.rwt.RWT; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.TabItem; @@ -65,15 +64,8 @@ public class FormBuilder { this.pageContext = pageContext; this.form = new Form(pageService.getJSONMapper()); - this.formParent = new Composite(pageContext.getParent(), SWT.NONE); - final GridLayout layout = new GridLayout(rows, true); - layout.horizontalSpacing = 10; - layout.verticalSpacing = 10; - layout.marginBottom = 50; - layout.marginLeft = 10; - layout.marginTop = 0; - this.formParent.setLayout(layout); - this.formParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + this.formParent = this.widgetFactory + .formGrid(pageContext.getParent(), rows); } public FormBuilder readonly(final boolean readonly) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/InputField.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/InputField.java index 3b6d8893..4349aabd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/InputField.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/InputField.java @@ -22,6 +22,10 @@ public interface InputField { void initValue(Collection values); + void initValue(final String value, final Integer listIndex); + + String getValue(); + void showError(String errorMessage); void clearError(); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/InputFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/InputFieldBuilder.java index 1c01dce3..f672847a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/InputFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/InputFieldBuilder.java @@ -17,6 +17,7 @@ import org.eclipse.swt.widgets.Label; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; +import ch.ethz.seb.sebserver.gui.service.examconfig.impl.InputFieldBuilderSupplier; import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; @@ -24,6 +25,18 @@ public interface InputFieldBuilder { String RES_BUNDLE_KEY_PREFIX = "sebserver.examconfig.attribute."; + /** Called by the InputFieldBuilderSupplier bean instance on initialization to avoid + * circular dependencies. + * + * This method must not be called from other then InputFieldBuilderSupplier + * For default this does nothing and a InputFieldBuilder that uses a reference to + * the calling InputFieldBuilderSupplier must override this to get the reference + * + * @param inputFieldBuilderSupplier reference of InputFieldBuilderSupplier */ + default void init(final InputFieldBuilderSupplier inputFieldBuilderSupplier) { + // NOOP for default + } + boolean builderFor( ConfigurationAttribute attribute, Orientation orientation); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/ValueChangeListener.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/ValueChangeListener.java index 26ef27a3..155806f2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/ValueChangeListener.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/ValueChangeListener.java @@ -9,7 +9,7 @@ package ch.ethz.seb.sebserver.gui.service.examconfig; 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.ConfigurationTableValues; import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext; public interface ValueChangeListener { @@ -20,6 +20,6 @@ public interface ValueChangeListener { String value, int listIndex); - void tableChanged(ConfigurationTableValue tableValue); + void tableChanged(ConfigurationTableValues tableValue); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/AbstractInputField.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/AbstractInputField.java index 81bf9635..c2d51700 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/AbstractInputField.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/AbstractInputField.java @@ -90,12 +90,14 @@ public abstract class AbstractInputField implements InputFiel values.stream() .filter(a -> this.attribute.id.equals(a.attributeId)) .findFirst() - .map(v -> { - this.initValue = v.value; - this.listIndex = (v.listIndex != null) ? v.listIndex : 0; - setValueToControl(this.initValue); - return this.initValue; - }); + .ifPresent(v -> initValue(v.value, v.listIndex)); + } + + @Override + public void initValue(final String value, final Integer listIndex) { + this.initValue = value; + this.listIndex = (listIndex != null) ? listIndex : 0; + setValueToControl(this.initValue); } protected void setDefaultValue() { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/CheckBoxBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/CheckBoxBuilder.java index ceb2b27b..2ef55fcd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/CheckBoxBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/CheckBoxBuilder.java @@ -16,6 +16,7 @@ import org.eclipse.swt.widgets.Composite; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; +import ch.ethz.seb.sebserver.gbl.Constants; 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.Orientation; @@ -66,7 +67,7 @@ public class CheckBoxBuilder implements InputFieldBuilder { return new CheckboxField( attribute, - viewContext.attributeMapping.getOrientation(attribute.id), + viewContext.getOrientation(attribute.id), checkbox); } @@ -84,6 +85,13 @@ public class CheckBoxBuilder implements InputFieldBuilder { protected void setValueToControl(final String value) { this.control.setSelection(Boolean.valueOf(this.initValue)); } + + @Override + public String getValue() { + return this.control.getSelection() + ? Constants.TRUE_STRING + : Constants.FALSE_STRING; + } } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ExamConfigurationServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ExamConfigurationServiceImpl.java index b40aaa62..51a26e4e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ExamConfigurationServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ExamConfigurationServiceImpl.java @@ -11,7 +11,6 @@ package ch.ethz.seb.sebserver.gui.service.examconfig.impl; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.NoSuchElementException; import java.util.stream.Collectors; import org.apache.tomcat.util.buf.StringUtils; @@ -31,7 +30,7 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration; 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.ConfigurationTableValues; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; import ch.ethz.seb.sebserver.gbl.model.sebconfig.View; @@ -51,7 +50,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.Ge import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurationValues; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetOrientations; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetViewList; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigTableValue; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigTableValues; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigValue; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @@ -66,20 +65,20 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService { private final JSONMapper jsonMapper; private final WidgetFactory widgetFactory; - private final Collection inputFieldBuilder; + private final InputFieldBuilderSupplier inputFieldBuilderSupplier; private final Collection valueChangeRules; protected ExamConfigurationServiceImpl( final RestService restService, final JSONMapper jsonMapper, final WidgetFactory widgetFactory, - final Collection inputFieldBuilder, + final InputFieldBuilderSupplier inputFieldBuilderSupplier, final Collection valueChangeRules) { this.restService = restService; this.jsonMapper = jsonMapper; this.widgetFactory = widgetFactory; - this.inputFieldBuilder = Utils.immutableCollectionOf(inputFieldBuilder); + this.inputFieldBuilderSupplier = inputFieldBuilderSupplier; this.valueChangeRules = Utils.immutableCollectionOf(valueChangeRules); } @@ -93,11 +92,7 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService { final ConfigurationAttribute attribute, final Orientation orientation) { - return this.inputFieldBuilder - .stream() - .filter(b -> b.builderFor(attribute, orientation)) - .findFirst() - .orElseThrow(() -> new NoSuchElementException("No InputFieldBuilder found for : " + attribute.type)); + return this.inputFieldBuilderSupplier.getInputFieldBuilder(attribute, orientation); } @Override @@ -169,7 +164,7 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService { @Override public Composite createViewGrid(final Composite parent, final ViewContext viewContext) { final Composite composite = new Composite(parent, SWT.NONE); - final GridLayout gridLayout = new GridLayout(viewContext.columns, true); + final GridLayout gridLayout = new GridLayout(viewContext.getColumns(), true); gridLayout.verticalSpacing = 0; composite.setLayout(gridLayout); composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); @@ -179,7 +174,7 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService { viewContext, this); - for (final ConfigurationAttribute attribute : viewContext.attributeMapping.getAttributes()) { + for (final ConfigurationAttribute attribute : viewContext.getAttributes()) { viewGridBuilder.add(attribute); } @@ -269,8 +264,8 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService { } @Override - public void tableChanged(final ConfigurationTableValue tableValue) { - this.restService.getBuilder(SaveExamConfigTableValue.class) + public void tableChanged(final ConfigurationTableValues tableValue) { + this.restService.getBuilder(SaveExamConfigTableValues.class) .withBody(tableValue) .call(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/InputFieldBuilderSupplier.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/InputFieldBuilderSupplier.java new file mode 100644 index 00000000..09ade7f2 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/InputFieldBuilderSupplier.java @@ -0,0 +1,47 @@ +/* + * 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.gui.service.examconfig.impl; + +import java.util.Collection; +import java.util.NoSuchElementException; + +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.Orientation; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder; + +@Lazy +@Service +@GuiProfile +public class InputFieldBuilderSupplier { + + private final Collection inputFieldBuilder; + + protected InputFieldBuilderSupplier(final Collection inputFieldBuilder) { + this.inputFieldBuilder = inputFieldBuilder; + inputFieldBuilder + .stream() + .forEach(builder -> builder.init(this)); + } + + public InputFieldBuilder getInputFieldBuilder( + final ConfigurationAttribute attribute, + final Orientation orientation) { + + return this.inputFieldBuilder + .stream() + .filter(b -> b.builderFor(attribute, orientation)) + .findFirst() + .orElseThrow(() -> new NoSuchElementException("No InputFieldBuilder found for : " + attribute.type)); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/LabelBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/LabelBuilder.java index f362e82f..28b75abd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/LabelBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/LabelBuilder.java @@ -55,7 +55,7 @@ public class LabelBuilder implements InputFieldBuilder { return new LabelField( attribute, - viewContext.attributeMapping.getOrientation(attribute.id), + viewContext.getOrientation(attribute.id), label); } @@ -74,6 +74,11 @@ public class LabelBuilder implements InputFieldBuilder { // Does Nothing, Label has no default value } + @Override + public String getValue() { + return this.control.getText(); + } + } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/PassworFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/PassworFieldBuilder.java index 3aa17356..11e3a4e0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/PassworFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/PassworFieldBuilder.java @@ -61,7 +61,7 @@ public class PassworFieldBuilder implements InputFieldBuilder { final ConfigurationAttribute attribute, final ViewContext viewContext) { - final Orientation orientation = viewContext.attributeMapping + final Orientation orientation = viewContext .getOrientation(attribute.id); final Composite innerGrid = InputFieldBuilder .createInnerGrid(parent, orientation); @@ -96,15 +96,7 @@ public class PassworFieldBuilder implements InputFieldBuilder { return; } - String hashedPWD; - try { - hashedPWD = hashPassword(pwd); - } catch (final NoSuchAlgorithmException e) { - log.error("Failed to hash password: ", e); - passwordInputField.showError("Failed to hash password"); - hashedPWD = null; - } - + final String hashedPWD = passwordInputField.getValue(); if (hashedPWD != null) { passwordInputField.clearError(); viewContext.getValueChangeListener().valueChanged( @@ -122,14 +114,6 @@ public class PassworFieldBuilder implements InputFieldBuilder { return passwordInputField; } - private String hashPassword(final String pwd) throws NoSuchAlgorithmException { - final MessageDigest digest = MessageDigest.getInstance("SHA-256"); - final byte[] encodedhash = digest.digest( - pwd.getBytes(StandardCharsets.UTF_8)); - - return Hex.encodeHexString(encodedhash); - } - static final class PasswordInputField extends AbstractInputField { private final Text confirm; @@ -154,6 +138,28 @@ public class PassworFieldBuilder implements InputFieldBuilder { } } + @Override + public String getValue() { + String hashedPWD; + try { + hashedPWD = hashPassword(this.control.getText()); + } catch (final NoSuchAlgorithmException e) { + log.error("Failed to hash password: ", e); + showError("Failed to hash password"); + hashedPWD = null; + } + + return hashedPWD; + } + + private String hashPassword(final String pwd) throws NoSuchAlgorithmException { + final MessageDigest digest = MessageDigest.getInstance("SHA-256"); + final byte[] encodedhash = digest.digest( + pwd.getBytes(StandardCharsets.UTF_8)); + + return Hex.encodeHexString(encodedhash); + } + } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableContext.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableContext.java new file mode 100644 index 00000000..48f034d7 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableContext.java @@ -0,0 +1,186 @@ +/* + * 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.gui.service.examconfig.impl; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; +import ch.ethz.seb.sebserver.gui.service.examconfig.InputField; +import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder; +import ch.ethz.seb.sebserver.gui.service.examconfig.ValueChangeListener; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigTableRowValues; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; + +public class TableContext { + + private static final Logger log = LoggerFactory.getLogger(TableContext.class); + + private final InputFieldBuilderSupplier inputFieldBuilderSupplier; + private final WidgetFactory widgetFactory; + private final RestService restService; + + public final ConfigurationAttribute attribute; + public final Orientation orientation; + + private final List rowAttributes; + private final List columnAttributes; + private final ViewContext viewContext; + + public TableContext( + final InputFieldBuilderSupplier inputFieldBuilderSupplier, + final WidgetFactory widgetFactory, + final RestService restService, + final ConfigurationAttribute attribute, + final ViewContext viewContext) { + + this.inputFieldBuilderSupplier = Objects.requireNonNull(inputFieldBuilderSupplier); + this.widgetFactory = Objects.requireNonNull(widgetFactory); + this.restService = Objects.requireNonNull(restService); + this.attribute = Objects.requireNonNull(attribute); + this.viewContext = Objects.requireNonNull(viewContext); + + this.orientation = viewContext + .getOrientation(attribute.id); + + this.rowAttributes = viewContext.getChildAttributes(attribute.id) + .stream() + .sorted(rowAttributeComparator(viewContext)) + .collect(Collectors.toList()); + + this.columnAttributes = this.rowAttributes + .stream() + .filter(attr -> viewContext.getOrientation(attr.id).xPosition > 0) + .sorted(columnAttributeComparator(viewContext)) + .collect(Collectors.toList()); + } + + public InputFieldBuilderSupplier getInputFieldBuilderSupplier() { + return this.inputFieldBuilderSupplier; + } + + public WidgetFactory getWidgetFactory() { + return this.widgetFactory; + } + + public ConfigurationAttribute getAttribute() { + return this.attribute; + } + + public Orientation getOrientation() { + return this.orientation; + } + + public Orientation getOrientation(final Long attributeId) { + return this.viewContext.getOrientation(attributeId); + } + + public List getRowAttributes() { + return this.rowAttributes; + } + + public List getColumnAttributes() { + return this.columnAttributes; + } + + public ViewContext getViewContext() { + return this.viewContext; + } + + public ValueChangeListener getValueChangeListener() { + return this.viewContext.getValueChangeListener(); + } + + public Long getInstitutionId() { + return this.viewContext.getInstitutionId(); + } + + public Long getConfigurationId() { + return this.viewContext.getConfigurationId(); + } + + public ConfigurationAttribute getAttribute(final Long attributeId) { + return this.viewContext.getAttribute(attributeId); + } + + public void flushInputFields(final Set attributeIds) { + this.viewContext.flushInputFields(attributeIds); + } + + public InputFieldBuilder getInputFieldBuilder( + final ConfigurationAttribute attribute2, + final Orientation orientation) { + + return this.inputFieldBuilderSupplier.getInputFieldBuilder(attribute2, orientation); + } + + public Map getTableRowValues(final int index) { + return this.restService.getBuilder(GetExamConfigTableRowValues.class) + .withQueryParam( + Domain.CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID, + this.attribute.getModelId()) + .withQueryParam( + Domain.CONFIGURATION_VALUE.ATTR_CONFIGURATION_ID, + String.valueOf(this.getConfigurationId())) + .withQueryParam( + Domain.CONFIGURATION_VALUE.ATTR_LIST_INDEX, + String.valueOf(index)) + .call() + .get( + error -> log.error("Failed to get table row values: ", error), + () -> Collections.emptyList()) + .stream() + .collect(Collectors.toMap( + val -> val.attributeId, + val -> TableValue.of(val))); + } + + public void registerInputField(final InputField inputField) { + this.viewContext.registerInputField(inputField); + } + + private Comparator rowAttributeComparator(final ViewContext viewContext) { + return (a1, a2) -> { + try { + final Orientation o1 = viewContext.getOrientation(a1.id); + final Orientation o2 = viewContext.getOrientation(a2.id); + return o1.yPosition.compareTo(o2.yPosition); + } catch (final Exception e) { + log.warn("Failed to get Orientations of ConfigurationAttribute to compare: ", e); + return -1; + } + }; + } + + private Comparator columnAttributeComparator(final ViewContext viewContext) { + return (a1, a2) -> { + try { + final Orientation o1 = viewContext.getOrientation(a1.id); + final Orientation o2 = viewContext.getOrientation(a2.id); + return o1.xPosition.compareTo(o2.xPosition); + } catch (final Exception e) { + log.warn("Failed to get Orientations of ConfigurationAttribute to compare: ", e); + return -1; + } + }; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/table/TableFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java similarity index 70% rename from src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/table/TableFieldBuilder.java rename to src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java index 57bc2efb..f3e0df5a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/table/TableFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package ch.ethz.seb.sebserver.gui.service.examconfig.impl.table; +package ch.ethz.seb.sebserver.gui.service.examconfig.impl; import java.util.ArrayList; import java.util.Collection; @@ -31,19 +31,18 @@ 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.ConfigurationTableValue; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue.TableValue; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService; import ch.ethz.seb.sebserver.gui.service.examconfig.InputField; import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder; -import ch.ethz.seb.sebserver.gui.service.examconfig.impl.AbstractInputField; -import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext; import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; @@ -56,12 +55,23 @@ public class TableFieldBuilder implements InputFieldBuilder { private static final String ROW_VALUE_KEY = "RowValues"; + private final RestService restService; private final WidgetFactory widgetFactory; + private InputFieldBuilderSupplier inputFieldBuilderSupplier; - public TableFieldBuilder(final WidgetFactory widgetFactory) { + protected TableFieldBuilder( + final RestService restService, + final WidgetFactory widgetFactory) { + + this.restService = restService; this.widgetFactory = widgetFactory; } + @Override + public void init(final InputFieldBuilderSupplier inputFieldBuilderSupplier) { + this.inputFieldBuilderSupplier = inputFieldBuilderSupplier; + } + @Override public boolean builderFor( final ConfigurationAttribute attribute, @@ -81,31 +91,26 @@ public class TableFieldBuilder implements InputFieldBuilder { final ViewContext viewContext) { final I18nSupport i18nSupport = viewContext.getI18nSupport(); - - final Orientation orientation = viewContext.attributeMapping - .getOrientation(attribute.id); - final List childAttributes = - viewContext.attributeMapping.childAttributeMapping.get(attribute.id); - final List columnAttributes = childAttributes - .stream() - .filter(attr -> viewContext.attributeMapping.getOrientation(attr.id).xPosition > 0) - .sorted((attr1, attr2) -> viewContext.attributeMapping.getOrientation(attr1.id).xPosition.compareTo( - viewContext.attributeMapping.getOrientation(attr2.id).xPosition)) - .collect(Collectors.toList()); + final TableContext tableContext = new TableContext( + this.inputFieldBuilderSupplier, + this.widgetFactory, + this.restService, + attribute, + viewContext); final Table table = new Table(parent, SWT.NONE | SWT.H_SCROLL); table.setLayout(new GridLayout()); final GridData gridData = new GridData( SWT.FILL, SWT.FILL, true, false, - (orientation != null) ? orientation.width() : 1, - (orientation != null) ? orientation.height() : 1); - gridData.heightHint = orientation.height * 40; + (tableContext.orientation != null) ? tableContext.orientation.width() : 1, + (tableContext.orientation != null) ? tableContext.orientation.height() : 1); + gridData.heightHint = tableContext.orientation.height * 40; table.setLayoutData(gridData); table.setHeaderVisible(true); table.addListener(SWT.Resize, this::adaptColumnWidth); - for (final ConfigurationAttribute columnAttribute : columnAttributes) { + for (final ConfigurationAttribute columnAttribute : tableContext.getColumnAttributes()) { final TableColumn column = new TableColumn(table, SWT.NONE); final String text = i18nSupport.getText( ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + columnAttribute.name, @@ -116,13 +121,8 @@ public class TableFieldBuilder implements InputFieldBuilder { } final TableInputField tableField = new TableInputField( - this.widgetFactory, - attribute, - orientation, - table, - childAttributes, - columnAttributes, - viewContext); + tableContext, + table); TableColumn column = new TableColumn(table, SWT.NONE); column.setImage(ImageIcon.ADD_BOX.getImage(parent.getDisplay())); @@ -175,31 +175,21 @@ public class TableFieldBuilder implements InputFieldBuilder { static final class TableInputField extends AbstractInputField { - private final List childAttributes; - private final List columnAttributes; - private final ViewContext viewContext; - private final WidgetFactory widgetFactory; + private final TableContext tableContext; private List> values; TableInputField( - final WidgetFactory widgetFactory, - final ConfigurationAttribute attribute, - final Orientation orientation, - final Table control, - final List childAttributes, - final List columnAttributes, - final ViewContext viewContext) { + final TableContext tableContext, + final Table control) { - super(attribute, orientation, control, null); - this.childAttributes = childAttributes; - this.columnAttributes = columnAttributes; - this.viewContext = viewContext; - this.widgetFactory = widgetFactory; + super(tableContext.attribute, tableContext.orientation, control, null); + this.tableContext = tableContext; } @Override public void initValue(final Collection values) { + clearTable(); // get all child values as TableValues final List tableValues = values.stream() .filter(this::isChildValue) @@ -229,22 +219,21 @@ public class TableFieldBuilder implements InputFieldBuilder { private boolean isChildValue(final ConfigurationValue value) { return this.attribute.id.equals( - this.viewContext.attributeMapping.attributeIdMapping - .get(value.attributeId).parentId); + this.tableContext.getAttribute(value.attributeId).parentId); } private void deleteRow(final int selectionIndex) { this.control.remove(selectionIndex); this.values.remove(selectionIndex); // send new values to web-service - this.viewContext.getValueChangeListener() + this.tableContext.getValueChangeListener() .tableChanged(extractTableValue()); } private void addRow() { final int index = this.values.size(); // create new values form default values - final Map rowValues = this.childAttributes + final Map rowValues = this.tableContext.getRowAttributes() .stream() .map(attr -> new TableValue(attr.id, index, attr.defaultValue)) .collect(Collectors.toMap( @@ -254,6 +243,9 @@ public class TableFieldBuilder implements InputFieldBuilder { this.values.add(rowValues); addTableRow(rowValues); this.control.layout(); + // send new values to web-service + this.tableContext.getValueChangeListener() + .tableChanged(extractTableValue()); } private void addTableRow(final Map rowValues) { @@ -278,88 +270,105 @@ public class TableFieldBuilder implements InputFieldBuilder { } - private void applyRowValues( - final int rowIndex, - final Map rowValues) { - - // set the new values - this.values.set(rowIndex, rowValues); - // update table row - applyTableRowValues(rowIndex); - - } +// private void applyRowValues( +// final int rowIndex, +// final Map rowValues) { +// +// // set the new values +// this.values.set(rowIndex, rowValues); +// // update table row +// applyTableRowValues(rowIndex); +// +// } private void applyTableRowValues(final int index) { final TableItem item = this.control.getItem(index); final Map rowValues = this.values.get(index); int cellIndex = 0; - for (final ConfigurationAttribute attr : this.columnAttributes) { - final String value = rowValues.containsKey(attr.id) - ? rowValues.get(attr.id).value - : null; - item.setText(cellIndex, value); + for (final ConfigurationAttribute attr : this.tableContext.getColumnAttributes()) { + if (rowValues.containsKey(attr.id)) { + item.setText(cellIndex, rowValues.get(attr.id).value); + } cellIndex++; } item.setData(ROW_VALUE_KEY, item); - - // send new values to web-service - this.viewContext.getValueChangeListener() - .tableChanged(extractTableValue()); } private void openForm(final int selectionIndex) { final Map rowValues = this.values.get(selectionIndex); - final ModalInputDialog> dialog = new ModalInputDialog<>( - this.control.getShell(), - this.widgetFactory); + final TableRowFormBuilder builder = new TableRowFormBuilder( + this.tableContext, + rowValues, + selectionIndex); - final TableRowFormBuilder builder = new TableRowFormBuilder(rowValues); - dialog.open( - new LocTextKey("Title"), - v -> System.out.println("Values Applied"), - builder); + new ModalInputDialog>( + this.control.getShell(), + this.tableContext.getWidgetFactory()) + .setDialogWidth(500) + .open( + new LocTextKey("Title"), + values -> applyFormValues(values, selectionIndex), + builder); } - private ConfigurationTableValue extractTableValue() { + private void applyFormValues(final Map values, final int index) { + final Map tableRowValues = this.tableContext.getTableRowValues(index); + if (tableRowValues == null || tableRowValues.isEmpty()) { + return; + } + + this.values.remove(index); + this.values.add(index, tableRowValues); + applyTableRowValues(index); + } + + private ConfigurationTableValues extractTableValue() { final List collect = this.values .stream() .flatMap(map -> map.values().stream()) .collect(Collectors.toList()); - return new ConfigurationTableValue( - this.viewContext.getInstitutionId(), - this.viewContext.getConfigurationId(), + return new ConfigurationTableValues( + this.tableContext.getInstitutionId(), + this.tableContext.getConfigurationId(), this.attribute.id, collect); - } @Override protected void setDefaultValue() { // NOTE this just empty the list for now // TODO do we need default values for lists? + clearTable(); + final List values = new ArrayList<>(); + this.tableContext.getValueChangeListener().tableChanged( + new ConfigurationTableValues( + this.tableContext.getInstitutionId(), + this.tableContext.getConfigurationId(), + this.attribute.id, + values)); + } + + private void clearTable() { this.control.setSelection(-1); if (this.control.getItemCount() > 0) { for (final TableItem item : this.control.getItems()) { item.dispose(); } } - - final List values = new ArrayList<>(); - this.viewContext.getValueChangeListener().tableChanged( - new ConfigurationTableValue( - this.viewContext.getInstitutionId(), - this.viewContext.getConfigurationId(), - this.attribute.id, - values)); } @Override protected void setValueToControl(final String value) { throw new UnsupportedOperationException(); } + + @Override + public String getValue() { + throw new UnsupportedOperationException(); + } } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableRowFormBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableRowFormBuilder.java new file mode 100644 index 00000000..59cd4c27 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableRowFormBuilder.java @@ -0,0 +1,124 @@ +/* + * 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.gui.service.examconfig.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +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.ConfigurationTableValues.TableValue; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; +import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService; +import ch.ethz.seb.sebserver.gui.service.examconfig.InputField; +import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialogComposer; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; + +public class TableRowFormBuilder implements ModalInputDialogComposer> { + + private final TableContext tableContext; + private final Map rowValues; + private final int listIndex; + + public TableRowFormBuilder( + final TableContext tableContext, + final Map rowValues, + final int listIndex) { + + this.tableContext = tableContext; + this.rowValues = rowValues; + this.listIndex = listIndex; + } + + @Override + public Supplier> compose(final Composite parent) { + + final List inputFields = new ArrayList<>(); + final Composite grid = this.tableContext + .getWidgetFactory() + .formGrid(parent, 2); + + final GridLayout layout = (GridLayout) grid.getLayout(); + layout.verticalSpacing = 0; + + for (final ConfigurationAttribute attribute : this.tableContext.getRowAttributes()) { + createLabel(grid, attribute); + inputFields.add(createInputField(grid, attribute)); + } + + // when the pop-up gets closed we have to remove the input fields from the view context + grid.addDisposeListener(event -> { + this.tableContext.flushInputFields(this.rowValues.keySet()); + }); + + return () -> inputFields.stream() + .map(field -> new TableValue( + field.getAttribute().id, + this.listIndex, + field.getValue())) + .collect(Collectors.toMap(tv -> tv.attributeId, Function.identity())); + } + + private InputField createInputField( + final Composite parent, + final ConfigurationAttribute attribute) { + + if (attribute.type == AttributeType.TABLE) { + throw new UnsupportedOperationException( + "Table type is currently not supported within a table row form view!"); + } + + final Orientation orientation = this.tableContext + .getOrientation(attribute.id); + + final InputFieldBuilder inputFieldBuilder = this.tableContext + .getInputFieldBuilder(attribute, orientation); + + final InputField inputField = inputFieldBuilder.createInputField( + parent, + attribute, + this.tableContext.getViewContext()); + + inputField.initValue( + this.rowValues.get(attribute.id).value, + this.listIndex); + + // we have to register the input field within the ViewContext to receive error messages + this.tableContext.registerInputField(inputField); + + return inputField; + } + + private void createLabel(final Composite parent, final ConfigurationAttribute attribute) { + final LocTextKey locTextKey = new LocTextKey( + ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + + attribute.name); + final Label label = this.tableContext + .getWidgetFactory() + .labelLocalized(parent, locTextKey, attribute.name); + final GridData gridData = new GridData(SWT.LEFT, SWT.TOP, true, false); + gridData.verticalIndent = 4; + label.setLayoutData(gridData); + label.setData(RWT.CUSTOM_VARIANT, CustomVariant.TITLE_LABEL.key); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TextFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TextFieldBuilder.java index 818ce68c..d1bee68d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TextFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TextFieldBuilder.java @@ -50,7 +50,7 @@ public class TextFieldBuilder implements InputFieldBuilder { final ConfigurationAttribute attribute, final ViewContext viewContext) { - final Orientation orientation = viewContext.attributeMapping + final Orientation orientation = viewContext .getOrientation(attribute.id); final Composite innerGrid = InputFieldBuilder .createInnerGrid(parent, orientation); @@ -100,6 +100,11 @@ public class TextFieldBuilder implements InputFieldBuilder { protected void setValueToControl(final String value) { this.control.setText(value); } + + @Override + public String getValue() { + return this.control.getText(); + } } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ViewContext.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ViewContext.java index d316b40e..41a0a203 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ViewContext.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ViewContext.java @@ -10,13 +10,13 @@ package ch.ethz.seb.sebserver.gui.service.examconfig.impl; import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Objects; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; import ch.ethz.seb.sebserver.gbl.model.sebconfig.View; import ch.ethz.seb.sebserver.gui.service.examconfig.InputField; import ch.ethz.seb.sebserver.gui.service.examconfig.ValueChangeListener; @@ -24,11 +24,11 @@ import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; public final class ViewContext { - public final Configuration configuration; - public final View view; - public final int columns, rows; + private final Configuration configuration; + private final View view; + private final int columns, rows; - public final AttributeMapping attributeMapping; + private final AttributeMapping attributeMapping; private final Map inputFieldMapping; private final ValueChangeListener valueChangeListener; private final I18nSupport i18nSupport; @@ -86,9 +86,36 @@ public final class ViewContext { return this.rows; } - public List getChildAttributes(final ConfigurationAttribute attribute) { - // TODO Auto-generated method stub - return null; + public Configuration getConfiguration() { + return this.configuration; + } + + public View getView() { + return this.view; + } +// +// public AttributeMapping getAttributeMapping() { +// return this.attributeMapping; +// } + + public Collection getChildAttributes(final Long id) { + return this.attributeMapping.childAttributeMapping.get(id); + } + + public Collection getAttributes() { + return this.attributeMapping.getAttributes(); + } + + public ConfigurationAttribute getAttribute(final Long attributeId) { + return this.attributeMapping.getAttribute(attributeId); + } + + public Collection getOrientationsOfGroup(final ConfigurationAttribute attribute) { + return this.attributeMapping.getOrientationsOfGroup(attribute); + } + + public Orientation getOrientation(final Long attributeId) { + return this.attributeMapping.getOrientation(attributeId); } public ValueChangeListener getValueChangeListener() { @@ -126,4 +153,16 @@ public final class ViewContext { .forEach(field -> field.initValue(values)); } + /** Removes all registered InputFields with the given attribute ids + * + * @param values Collection of attribute ids */ + void flushInputFields(final Collection values) { + if (values == null) { + return; + } + + values.stream() + .forEach(attrId -> this.inputFieldMapping.remove(attrId)); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ViewGridBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ViewGridBuilder.java index 1adaef33..78b70ba3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ViewGridBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ViewGridBuilder.java @@ -13,6 +13,7 @@ import java.util.HashSet; import java.util.Set; import org.apache.commons.lang3.StringUtils; +import org.eclipse.rap.rwt.RWT; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; @@ -30,6 +31,7 @@ import ch.ethz.seb.sebserver.gui.service.examconfig.InputField; import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; public class ViewGridBuilder { @@ -49,7 +51,7 @@ public class ViewGridBuilder { this.examConfigurationService = examConfigurationService; this.parent = parent; this.viewContext = viewContext; - this.grid = new CellFieldBuilderAdapter[viewContext.rows][viewContext.columns]; + this.grid = new CellFieldBuilderAdapter[viewContext.getRows()][viewContext.getColumns()]; this.registeredGroups = new HashSet<>(); } @@ -59,7 +61,7 @@ public class ViewGridBuilder { return this; } - final Orientation orientation = this.viewContext.attributeMapping + final Orientation orientation = this.viewContext .getOrientation(attribute.id); // create group builder @@ -101,6 +103,10 @@ public class ViewGridBuilder { } case LEFT: { this.grid[ypos][xpos - 1] = labelBuilder(attribute, orientation); + // special case for password, also add confirm label + if (attribute.type == AttributeType.PASSWORD_FIELD) { + this.grid[ypos + 1][xpos - 1] = passwordConfirmLabel(attribute, orientation); + } break; } case TOP: { @@ -179,10 +185,6 @@ public class ViewGridBuilder { final ConfigurationAttribute attribute, final Orientation orientation) { - if (attribute.type == AttributeType.PASSWORD_FIELD) { - return passwordConfirmLabel(attribute, orientation); - } - return new CellFieldBuilderAdapter() { @Override public void createCell(final ViewGridBuilder builder) { @@ -192,6 +194,7 @@ public class ViewGridBuilder { ViewGridBuilder.this.parent, new LocTextKey(ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name), attribute.name); + label.setData(RWT.CUSTOM_VARIANT, CustomVariant.TITLE_LABEL.key); final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false); switch (orientation.title) { @@ -211,6 +214,7 @@ public class ViewGridBuilder { } } label.setLayoutData(gridData); + } }; } @@ -232,6 +236,7 @@ public class ViewGridBuilder { label.setAlignment(SWT.LEFT); gridData.verticalIndent = 10; label.setLayoutData(gridData); + label.setData(RWT.CUSTOM_VARIANT, CustomVariant.TITLE_LABEL.key); } }; } @@ -254,7 +259,7 @@ public class ViewGridBuilder { this.builder = builder; this.attribute = attribute; this.orientationsOfGroup = - builder.viewContext.attributeMapping.getOrientationsOfGroup(attribute); + builder.viewContext.getOrientationsOfGroup(attribute); for (final Orientation o : this.orientationsOfGroup) { this.x = (this.x < o.xpos()) ? o.xpos() : this.x; this.x = (this.y < o.ypos()) ? o.ypos() : this.y; @@ -291,7 +296,7 @@ public class ViewGridBuilder { final Orientation orientation, final InputFieldBuilder inputFieldBuilder) { - final ConfigurationAttribute attr = this.builder.viewContext.attributeMapping + final ConfigurationAttribute attr = this.builder.viewContext .getAttribute(orientation.attributeId); final InputField inputField = inputFieldBuilder.createInputField( diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/table/TableRowFormBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/table/TableRowFormBuilder.java deleted file mode 100644 index 656b96c3..00000000 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/table/TableRowFormBuilder.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.gui.service.examconfig.impl.table; - -import java.util.Map; -import java.util.function.Supplier; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; - -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue.TableValue; -import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialogComposer; - -public class TableRowFormBuilder implements ModalInputDialogComposer> { - - private final Map rowValues; - - public TableRowFormBuilder(final Map rowValues) { - this.rowValues = rowValues; - } - - @Override - public Supplier> compose(final Composite parent) { - final Label test = new Label(parent, SWT.NONE); - test.setText("TEST"); - return () -> null; - } - -} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/I18nSupportImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/I18nSupportImpl.java index d794921d..75b98623 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/I18nSupportImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/I18nSupportImpl.java @@ -139,13 +139,10 @@ public class I18nSupportImpl implements I18nSupport { .toString(this.displayDateFormatter); final UserInfo userInfo = this.currentUser.get(); - if (userInfo.timeZone != null && !userInfo.timeZone.equals(DateTimeZone.UTC)) { - if (userInfo != null && userInfo.timeZone != null) { - return dateTimeStringUTC + date - .withZone(userInfo.timeZone) - .toString(this.timeZoneFormatter); - } - return dateTimeStringUTC; + if (userInfo != null && userInfo.timeZone != null && !userInfo.timeZone.equals(DateTimeZone.UTC)) { + return dateTimeStringUTC + date + .withZone(userInfo.timeZone) + .toString(this.timeZoneFormatter); } else { return dateTimeStringUTC; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModalInputDialog.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModalInputDialog.java index 4fd88ceb..91397778 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModalInputDialog.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModalInputDialog.java @@ -39,6 +39,7 @@ public class ModalInputDialog extends Dialog { new LocTextKey("sebserver.overall.action.close"); private final WidgetFactory widgetFactory; + private int dialogWidth = 400; public ModalInputDialog( final Shell parent, @@ -48,6 +49,11 @@ public class ModalInputDialog extends Dialog { this.widgetFactory = widgetFactory; } + public ModalInputDialog setDialogWidth(final int dialogWidth) { + this.dialogWidth = dialogWidth; + return this; + } + public void open( final LocTextKey title, final Consumer callback, @@ -65,6 +71,8 @@ public class ModalInputDialog extends Dialog { main.setLayout(new GridLayout()); final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true); gridData.horizontalSpan = 2; + gridData.widthHint = this.dialogWidth; + main.setLayoutData(gridData); final Supplier valueSuppier = contentComposer.compose(main); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/GetExamConfigTableRowValues.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/GetExamConfigTableRowValues.java new file mode 100644 index 00000000..d38e4259 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/GetExamConfigTableRowValues.java @@ -0,0 +1,42 @@ +/* + * 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.gui.service.remote.webservice.api.seb.examconfig; + +import java.util.Collection; + +import org.springframework.context.annotation.Lazy; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.type.TypeReference; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class GetExamConfigTableRowValues extends RestCall> { + + protected GetExamConfigTableRowValues() { + super(new TypeKey<>( + CallType.GET_LIST, + EntityType.CONFIGURATION_VALUE, + new TypeReference>() { + }), + HttpMethod.GET, + MediaType.APPLICATION_FORM_URLENCODED, + API.CONFIGURATION_VALUE_ENDPOINT + API.CONFIGURATION_TABLE_ROW_VALUE_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/SaveExamConfigTableValue.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/SaveExamConfigTableValues.java similarity index 83% rename from src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/SaveExamConfigTableValue.java rename to src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/SaveExamConfigTableValues.java index f5eb0d1a..c0c398be 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/SaveExamConfigTableValue.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/SaveExamConfigTableValues.java @@ -17,20 +17,20 @@ import com.fasterxml.jackson.core.type.TypeReference; import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; @Lazy @Component @GuiProfile -public class SaveExamConfigTableValue extends RestCall { +public class SaveExamConfigTableValues extends RestCall { - protected SaveExamConfigTableValue() { + protected SaveExamConfigTableValues() { super(new TypeKey<>( CallType.SAVE, EntityType.CONFIGURATION_VALUE, - new TypeReference() { + new TypeReference() { }), HttpMethod.PUT, MediaType.APPLICATION_JSON_UTF8, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java index 28aca998..e8921ca5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java @@ -175,6 +175,19 @@ public class WidgetFactory { return defaultPageLayout; } + public Composite formGrid(final Composite parent, final int rows) { + final Composite grid = new Composite(parent, SWT.NONE); + final GridLayout layout = new GridLayout(rows, true); + layout.horizontalSpacing = 10; + layout.verticalSpacing = 10; + layout.marginBottom = 50; + layout.marginLeft = 10; + layout.marginTop = 0; + grid.setLayout(layout); + grid.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + return grid; + } + public Button buttonLocalized(final Composite parent, final String locTextKey) { final Button button = new Button(parent, SWT.NONE); this.injectI18n(button, new LocTextKey(locTextKey)); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/PermissionDeniedException.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/PermissionDeniedException.java index 790c4424..7b01bb2b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/PermissionDeniedException.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/PermissionDeniedException.java @@ -12,6 +12,8 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType; import ch.ethz.seb.sebserver.gbl.model.GrantEntity; +/** Permission denied exception that refers to the checked entity type, privilege and + * the user identifier of the user that did request the permission */ public class PermissionDeniedException extends RuntimeException { private static final long serialVersionUID = 5333137812363042580L; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/SEBServerUser.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/SEBServerUser.java index 91ea8b17..1a4016b6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/SEBServerUser.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/SEBServerUser.java @@ -22,7 +22,10 @@ import org.springframework.util.CollectionUtils; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserRole; -/** SEBServerUser defines web-service internal user-account based authentication principal */ +/** SEBServerUser defines web-service internal user-account based authentication principal + * + * This implements Spring's UserDetails and CredentialsContainer to act as a principal + * within internal authentication and authorization processes. */ public final class SEBServerUser implements UserDetails, CredentialsContainer { private static final long serialVersionUID = 5726250141482925769L; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupportDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupportDAO.java index 89314de7..a68139d0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupportDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupportDAO.java @@ -108,7 +108,7 @@ public interface BulkActionSupportDAO { return bulkAction.sources .stream() .map(selectionFunction) // apply select function for each source key - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) // handle and skip results with error + .flatMap(DAOLoggingSupport::logAndSkipOnError) // handle and skip results with error .flatMap(Collection::stream) // Flatten stream of Collection in to one stream .collect(Collectors.toSet()); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ConfigurationValueDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ConfigurationValueDAO.java index 1e80c24a..138b02d1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ConfigurationValueDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ConfigurationValueDAO.java @@ -12,7 +12,7 @@ import java.util.Collection; import java.util.Set; import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -26,11 +26,17 @@ public interface ConfigurationValueDAO extends EntityDAO getTableValue( + Result getTableValues( final Long institutionId, - final Long attributeId, - final Long configurationId); + final Long configurationId, + final Long attributeId); - Result saveTableValue(ConfigurationTableValue value); + Result saveTableValues(ConfigurationTableValues value); + + Result> getTableRowValues( + final Long institutionId, + final Long configurationId, + final Long attributeId, + final Integer rowIndex); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/DAOLoggingSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/DAOLoggingSupport.java index 289959af..c86fc291 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/DAOLoggingSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/DAOLoggingSupport.java @@ -20,7 +20,7 @@ public final class DAOLoggingSupport { public static final Logger log = LoggerFactory.getLogger(DAOLoggingSupport.class); - public static Stream logUnexpectedErrorAndSkip(final Result result) { + public static Stream logAndSkipOnError(final Result result) { return Result.skipOnError( result.onError(error -> log.error("Unexpected error. Object processing is skipped: ", error))); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ResourceNotFoundException.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ResourceNotFoundException.java index 89069b03..b2cfe44e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ResourceNotFoundException.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ResourceNotFoundException.java @@ -20,6 +20,7 @@ public final class ResourceNotFoundException extends RuntimeException { private static final long serialVersionUID = 8319235723086949618L; + /** The entity key of the resource that was requested */ public final EntityKey entityKey; public ResourceNotFoundException(final EntityType entityType, final String modelId) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationAttributeDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationAttributeDAOImpl.java index ffab087f..337a1a1b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationAttributeDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationAttributeDAOImpl.java @@ -82,7 +82,7 @@ public class ConfigurationAttributeDAOImpl implements ConfigurationAttributeDAO .execute() .stream() .map(ConfigurationAttributeDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } @@ -105,7 +105,7 @@ public class ConfigurationAttributeDAOImpl implements ConfigurationAttributeDAO .execute() .stream() .map(ConfigurationAttributeDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .filter(predicate) .collect(Collectors.toList())); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOImpl.java index 49eada70..0fed03e1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOImpl.java @@ -84,7 +84,7 @@ public class ConfigurationDAOImpl implements ConfigurationDAO { .execute() .stream() .map(ConfigurationDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } @@ -113,7 +113,7 @@ public class ConfigurationDAOImpl implements ConfigurationDAO { .execute() .stream() .map(ConfigurationDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .filter(predicate) .collect(Collectors.toList())); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java index ea3cfc37..4af4ca77 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java @@ -98,7 +98,7 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO { .execute() .stream() .map(ConfigurationNodeDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } @@ -133,7 +133,7 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO { .execute() .stream() .map(ConfigurationNodeDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .filter(predicate) .collect(Collectors.toList())); } @@ -362,21 +362,18 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO { .execute() .stream() .forEach(attrRec -> { - final boolean bigValue = ConfigurationValueDAOImpl.isBigValue(attrRec); final String value = templateValues.getOrDefault( attrRec.getId(), attrRec.getDefaultValue()); - //if (StringUtils.isNoneBlank(value)) { this.configurationValueRecordMapper.insert(new ConfigurationValueRecord( null, configNode.institutionId, config.getId(), attrRec.getId(), 0, - bigValue ? null : value, - bigValue ? value : null)); - //} + value, + null)); }); return configNode; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationValueDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationValueDAOImpl.java index 34f48ee9..6b94abbb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationValueDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationValueDAOImpl.java @@ -13,6 +13,7 @@ import static org.mybatis.dynamic.sql.SqlBuilder.isIn; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -27,10 +28,8 @@ import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeValueType; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue.TableValue; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue; 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; @@ -101,7 +100,7 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { .execute() .stream() .map(ConfigurationValueDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .filter(predicate) .collect(Collectors.toList())); } @@ -116,7 +115,7 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { .execute() .stream() .map(ConfigurationValueDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } @@ -133,15 +132,14 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { final String value = (data.value != null) ? data.value : attributeRecord.getDefaultValue(); - final boolean bigValue = isBigValue(attributeRecord); final ConfigurationValueRecord newRecord = new ConfigurationValueRecord( null, data.institutionId, data.configurationId, data.attributeId, data.listIndex, - (bigValue) ? null : value, - (bigValue) ? value : null); + value, + null); this.configurationValueRecordMapper.insert(newRecord); return newRecord; @@ -178,15 +176,14 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { id = data.id; } - final boolean bigValue = isBigValue(attributeRecord); final ConfigurationValueRecord newRecord = new ConfigurationValueRecord( id, null, null, null, data.listIndex, - (bigValue) ? null : data.value, - (bigValue) ? data.value : null); + data.value, + null); this.configurationValueRecordMapper.updateByPrimaryKeySelective(newRecord); return this.configurationValueRecordMapper.selectByPrimaryKey(id); @@ -197,28 +194,15 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { @Override @Transactional(readOnly = true) - public Result getTableValue( + public Result getTableValues( final Long institutionId, - final Long attributeId, - final Long configurationId) { + final Long configurationId, + final Long attributeId) { return attributeRecordById(attributeId) - .map(attributeRecord -> { - - // get all attributes of the table (columns) - final List columnAttributes = - this.configurationAttributeRecordMapper.selectByExample() - .where( - ConfigurationAttributeRecordDynamicSqlSupport.parentId, - isEqualTo(attributeRecord.getId())) - .build() - .execute(); - - final Map attributeMapping = columnAttributes - .stream() - .collect(Collectors.toMap(attr -> attr.getId(), Function.identity())); - - // get all values of the table and group them by attribute and sorted by list/row index + .flatMap(this::getAttributeMapping) + .map(attributeMapping -> { + // get all values of the table final List values = this.configurationValueRecordMapper.selectByExample() .where( ConfigurationValueRecordDynamicSqlSupport.institutionId, @@ -232,10 +216,13 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { .build() .execute() .stream() - .map(value -> getTableValue(value, attributeMapping)) + .map(value -> new TableValue( + value.getConfigurationAttributeId(), + value.getListIndex(), + value.getValue())) .collect(Collectors.toList()); - return new ConfigurationTableValue( + return new ConfigurationTableValues( institutionId, configurationId, attributeId, @@ -243,23 +230,66 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { }); } - private TableValue getTableValue( - final ConfigurationValueRecord value, - final Map attributeMapping) { + @Override + public Result> getTableRowValues( + final Long institutionId, + final Long configurationId, + final Long attributeId, + final Integer rowIndex) { - final Long configurationAttributeId = value.getConfigurationAttributeId(); - final ConfigurationAttributeRecord configurationAttributeRecord = attributeMapping - .get(configurationAttributeId); - final boolean bigValue = isBigValue(configurationAttributeRecord); - return new TableValue( - value.getConfigurationAttributeId(), - value.getListIndex(), - bigValue ? value.getText() : value.getValue()); + return attributeRecordById(attributeId) + .flatMap(this::getAttributeMapping) + .map(attributeMapping -> { + + if (attributeMapping == null || attributeMapping.isEmpty()) { + return Collections.emptyList(); + } + + // get all values of the table for specified row + return this.configurationValueRecordMapper.selectByExample() + .where( + ConfigurationValueRecordDynamicSqlSupport.institutionId, + isEqualTo(institutionId)) + .and( + ConfigurationValueRecordDynamicSqlSupport.configurationId, + isEqualTo(configurationId)) + .and( + ConfigurationValueRecordDynamicSqlSupport.listIndex, + isEqualTo(rowIndex)) + .and( + ConfigurationValueRecordDynamicSqlSupport.configurationAttributeId, + SqlBuilder.isIn(new ArrayList<>(attributeMapping.keySet()))) + .build() + .execute() + .stream() + .map(ConfigurationValueDAOImpl::toDomainModel) + .flatMap(DAOLoggingSupport::logAndSkipOnError) + .collect(Collectors.toList()); + }); + } + + // get all attributes of the table (columns) mapped to attribute id + private Result> getAttributeMapping( + final ConfigurationAttributeRecord attributeRecord) { + + return Result.tryCatch(() -> { + final List columnAttributes = + this.configurationAttributeRecordMapper.selectByExample() + .where( + ConfigurationAttributeRecordDynamicSqlSupport.parentId, + isEqualTo(attributeRecord.getId())) + .build() + .execute(); + + return columnAttributes + .stream() + .collect(Collectors.toMap(attr -> attr.getId(), Function.identity())); + }); } @Override @Transactional - public Result saveTableValue(final ConfigurationTableValue value) { + public Result saveTableValues(final ConfigurationTableValues value) { return checkInstitutionalIntegrity(value) .map(this::checkFollowUpIntegrity) .flatMap(val -> attributeRecordById(val.attributeId)) @@ -294,15 +324,14 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { // then add the new values for (final TableValue tableValue : value.values) { final ConfigurationAttributeRecord columnAttr = attributeMap.get(tableValue.attributeId); - final boolean bigValue = isBigValue(columnAttr); final ConfigurationValueRecord valueRecord = new ConfigurationValueRecord( null, value.institutionId, value.configurationId, columnAttr.getId(), tableValue.listIndex, - (bigValue) ? null : tableValue.value, - (bigValue) ? tableValue.value : null); + tableValue.value, + null); this.configurationValueRecordMapper.insert(valueRecord); } @@ -342,23 +371,16 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { } private static Result toDomainModel(final ConfigurationValueRecord record) { - return Result.tryCatch(() -> new ConfigurationValue( - record.getId(), - record.getInstitutionId(), - record.getConfigurationId(), - record.getConfigurationAttributeId(), - record.getListIndex(), - record.getValue())); - } + return Result.tryCatch(() -> { - public static boolean isBigValue(final ConfigurationAttributeRecord attributeRecord) { - try { - final AttributeType type = AttributeType.valueOf(attributeRecord.getType()); - return type.attributeValueType == AttributeValueType.LARGE_TEXT - || type.attributeValueType == AttributeValueType.BASE64_BINARY; - } catch (final Exception e) { - return false; - } + return new ConfigurationValue( + record.getId(), + record.getInstitutionId(), + record.getConfigurationId(), + record.getConfigurationAttributeId(), + record.getListIndex(), + record.getValue()); + }); } private Result checkInstitutionalIntegrity(final ConfigurationValue data) { @@ -371,7 +393,7 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { }); } - private Result checkInstitutionalIntegrity(final ConfigurationTableValue data) { + private Result checkInstitutionalIntegrity(final ConfigurationTableValues data) { return Result.tryCatch(() -> { final ConfigurationRecord r = this.configurationRecordMapper.selectByPrimaryKey(data.configurationId); if (r.getInstitutionId().longValue() != data.institutionId.longValue()) { @@ -381,7 +403,7 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { }); } - private ConfigurationTableValue checkFollowUpIntegrity(final ConfigurationTableValue data) { + private ConfigurationTableValues checkFollowUpIntegrity(final ConfigurationTableValues data) { checkFollowUp(data.configurationId); return data; } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java index 60bb9cee..1d907fa0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java @@ -86,7 +86,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { .execute() .stream() .map(ExamConfigurationMapDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } @@ -112,7 +112,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { .execute() .stream() .map(ExamConfigurationMapDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .filter(predicate) .collect(Collectors.toList())); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java index 41f1ac09..247dd484 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java @@ -97,7 +97,7 @@ public class IndicatorDAOImpl implements IndicatorDAO { .execute() .stream() .map(this::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .filter(predicate) .collect(Collectors.toList()); }); @@ -113,7 +113,7 @@ public class IndicatorDAOImpl implements IndicatorDAO { .execute() .stream() .map(this::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } @@ -219,7 +219,7 @@ public class IndicatorDAOImpl implements IndicatorDAO { .execute() .stream() .map(this::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/InstitutionDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/InstitutionDAOImpl.java index 291b6679..25125a5e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/InstitutionDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/InstitutionDAOImpl.java @@ -87,7 +87,7 @@ public class InstitutionDAOImpl implements InstitutionDAO { return records.stream() .map(InstitutionDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } @@ -110,7 +110,7 @@ public class InstitutionDAOImpl implements InstitutionDAO { .execute() .stream() .map(InstitutionDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .filter(predicate) .collect(Collectors.toList())); } @@ -244,7 +244,7 @@ public class InstitutionDAOImpl implements InstitutionDAO { .execute() .stream() .map(InstitutionDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java index 85ce462e..5b409160 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java @@ -94,7 +94,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO { return records.stream() .map(this::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } @@ -125,7 +125,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO { .execute() .stream() .map(this::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .filter(predicate) .collect(Collectors.toList()); }); @@ -266,7 +266,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO { .execute() .stream() .map(this::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/OrientationDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/OrientationDAOImpl.java index 5a9cbea3..bef328d6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/OrientationDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/OrientationDAOImpl.java @@ -72,7 +72,7 @@ public class OrientationDAOImpl implements OrientationDAO { .execute() .stream() .map(OrientationDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } @@ -98,7 +98,7 @@ public class OrientationDAOImpl implements OrientationDAO { .execute() .stream() .map(OrientationDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .filter(predicate) .collect(Collectors.toList())); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SebClientConfigDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SebClientConfigDAOImpl.java index 2221ffa2..91156ad2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SebClientConfigDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SebClientConfigDAOImpl.java @@ -94,7 +94,7 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO { return records.stream() .map(SebClientConfigDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } @@ -125,7 +125,7 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO { .execute() .stream() .map(SebClientConfigDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .filter(predicate) .collect(Collectors.toList()); }); @@ -246,7 +246,7 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO { .execute() .stream() .map(SebClientConfigDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java index 7582767d..ff903a06 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java @@ -243,7 +243,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO { .execute() .stream() .map(UserActivityLogDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } @@ -312,7 +312,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO { .execute() .stream() .map(UserActivityLogDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .filter(_predicate) .collect(Collectors.toList()); @@ -329,7 +329,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO { .execute() .stream() .map(UserActivityLogDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java index 12a38d2f..0704565a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java @@ -146,7 +146,7 @@ public class UserDAOImpl implements UserDAO { return records.stream() .map(this::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } @@ -184,7 +184,7 @@ public class UserDAOImpl implements UserDAO { .execute() .stream() .map(this::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .filter(_predicate) .collect(Collectors.toList()); }); @@ -362,7 +362,7 @@ public class UserDAOImpl implements UserDAO { .execute() .stream() .map(this::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ViewDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ViewDAOImpl.java index 74887adf..bdb6dee4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ViewDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ViewDAOImpl.java @@ -69,7 +69,7 @@ public class ViewDAOImpl implements ViewDAO { .execute() .stream() .map(ViewDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()); }); } @@ -89,7 +89,7 @@ public class ViewDAOImpl implements ViewDAO { .execute() .stream() .map(ViewDAOImpl::toDomainModel) - .flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) + .flatMap(DAOLoggingSupport::logAndSkipOnError) .filter(predicate) .collect(Collectors.toList())); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebExamConfigService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebExamConfigService.java index b90d452e..f1b5a342 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebExamConfigService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebExamConfigService.java @@ -8,13 +8,13 @@ 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.ConfigurationTableValues; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; public interface SebExamConfigService { void validate(ConfigurationValue value); - void validate(ConfigurationTableValue tableValue); + void validate(ConfigurationTableValues tableValue); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebExamConfigServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebExamConfigServiceImpl.java index fe7049c0..e51d96f1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebExamConfigServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebExamConfigServiceImpl.java @@ -9,13 +9,14 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; import java.util.Collection; -import java.util.Objects; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; 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.ConfigurationTableValues; 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; @@ -27,6 +28,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigServ @WebServiceProfile public class SebExamConfigServiceImpl implements SebExamConfigService { + private static final Logger log = LoggerFactory.getLogger(SebExamConfigServiceImpl.class); + private final ConfigurationAttributeDAO configurationAttributeDAO; private final Collection validators; @@ -40,7 +43,10 @@ public class SebExamConfigServiceImpl implements SebExamConfigService { @Override public void validate(final ConfigurationValue value) { - Objects.requireNonNull(value); + if (value == null) { + log.warn("Validate called with null reference. Ignore this and skip validation"); + return; + } final ConfigurationAttribute attribute = this.configurationAttributeDAO.byPK(value.attributeId) .getOrThrow(); @@ -53,7 +59,7 @@ public class SebExamConfigServiceImpl implements SebExamConfigService { } @Override - public void validate(final ConfigurationTableValue tableValue) { + public void validate(final ConfigurationTableValues tableValue) { // TODO Auto-generated method stub } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IllegalAPIArgumentException.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIConstraintViolationException.java similarity index 51% rename from src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IllegalAPIArgumentException.java rename to src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIConstraintViolationException.java index 5f767572..6a8ce219 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IllegalAPIArgumentException.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIConstraintViolationException.java @@ -8,19 +8,21 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; -public class IllegalAPIArgumentException extends RuntimeException { +/** This exception shall be used on additional validations of API attribute constraints. + * Throwing an APIConstraintViolationException will lead to a HTTP 400 Bad Request response. */ +public class APIConstraintViolationException extends RuntimeException { private static final long serialVersionUID = 3732727447520974727L; - public IllegalAPIArgumentException() { + public APIConstraintViolationException() { super(); } - public IllegalAPIArgumentException(final String message, final Throwable cause) { + public APIConstraintViolationException(final String message, final Throwable cause) { super(message, cause); } - public IllegalAPIArgumentException(final String message) { + public APIConstraintViolationException(final String message) { super(message); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java index ce5d69df..b0553168 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java @@ -111,9 +111,9 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler { .createErrorResponse(ex.getMessage()); } - @ExceptionHandler(IllegalAPIArgumentException.class) + @ExceptionHandler(APIConstraintViolationException.class) public ResponseEntity handleIllegalAPIArgumentException( - final IllegalAPIArgumentException ex, + final APIConstraintViolationException ex, final WebRequest request) { log.warn("Illegal API Argument Exception: ", ex); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java index 9e713b55..03069697 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java @@ -8,6 +8,7 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; +import java.util.Collection; import java.util.Objects; import javax.validation.Valid; @@ -24,7 +25,7 @@ import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; -import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues; 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; @@ -90,7 +91,7 @@ public class ConfigurationValueController extends EntityController this.configurationValueDAO.getTableValue( + .flatMap(config -> this.configurationValueDAO.getTableValues( config.institutionId, + configurationId, + attributeId)) + .getOrThrow(); + } + + @RequestMapping( + path = API.CONFIGURATION_TABLE_ROW_VALUE_PATH_SEGMENT, + method = RequestMethod.GET, + consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, + produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public Collection getTableRowValueBy( + @RequestParam( + name = Domain.CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID, + required = true) final Long attributeId, + @RequestParam( + name = Domain.CONFIGURATION_VALUE.ATTR_CONFIGURATION_ID, + required = true) final Long configurationId, + @RequestParam( + name = Domain.CONFIGURATION_VALUE.ATTR_LIST_INDEX, + required = true) final Integer rowIndex) { + + return this.configurationDAO.byPK(configurationId) + .flatMap(this.authorization::checkRead) + .flatMap(config -> this.configurationValueDAO.getTableRowValues( + config.institutionId, + configurationId, attributeId, - configurationId)) + rowIndex)) .getOrThrow(); } @@ -112,12 +139,12 @@ public class ConfigurationValueController extends EntityController this.configurationValueDAO.saveTableValue(tableValue)) + .flatMap(config -> this.configurationValueDAO.saveTableValues(tableValue)) .getOrThrow(); } @@ -149,7 +176,7 @@ public class ConfigurationValueController extends EntityController { return this.beanValidationService.validateBean(entity); } else { return Result.ofError( - new IllegalAPIArgumentException("Model identifier already defined: " + entity.getModelId())); + new APIConstraintViolationException("Model identifier already defined: " + entity.getModelId())); } } @@ -363,7 +363,7 @@ public abstract class EntityController { if (entity.getModelId() != null) { return Result.of(entity); } else { - return Result.ofError(new IllegalAPIArgumentException("Missing model identifier")); + return Result.ofError(new APIConstraintViolationException("Missing model identifier")); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/SebClientConfigController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/SebClientConfigController.java index 3be80ed4..ab976f97 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/SebClientConfigController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/SebClientConfigController.java @@ -98,7 +98,7 @@ public class SebClientConfigController extends ActivatableEntityController - - - sebMode - 1 - sebServerFallback - - sebServerURL - http://localhost:8080 - sebServerConfiguration - - - institution - 1 - clientName - +Ra01]`-4e*$(=@= - clientSecret - (+8Jl-#l8t(XHi%hxi88HXsjC{BE1F(TgdbQSzz=TpR]%_fJ~IjkdAL^YzfV*()t - accessTokenEndpoint - /oauth/token - handshakeEndpoint - /exam-api/v1/handshake - examConfigEndpoint - /exam-api/v1/examconfig - pingEndpoint - /exam-api/v1/sebping - eventEndpoint - /exam-api/v1/sebevent - - - - \ No newline at end of file