SEBSERV-44 SEBSERV-45 exam config table implementation

This commit is contained in:
anhefti 2019-05-10 14:09:56 +02:00
parent e2b93e5529
commit 8867721a8a
56 changed files with 906 additions and 377 deletions

View file

@ -112,6 +112,7 @@
<excludes> <excludes>
<exclude>**/batis/mapper/*.java</exclude> <exclude>**/batis/mapper/*.java</exclude>
<exclude>**/batis/model/*.java</exclude> <exclude>**/batis/model/*.java</exclude>
<exclude name="UselessParentheses"/>
</excludes> </excludes>
</configuration> </configuration>
<executions> <executions>

View file

@ -71,6 +71,8 @@ public final class API {
public static final String CONFIGURATION_VALUE_ENDPOINT = "/configuration_value"; 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_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"; public static final String CONFIGURATION_ATTRIBUTE_ENDPOINT = "/configuration_attribute";

View file

@ -24,11 +24,17 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.util.Utils; 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 { public class APIMessage implements Serializable {
private static final long serialVersionUID = -6858683658311637361L; 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 { public enum ErrorMessage {
/** For every unknown or unspecific internal error */
GENERIC("0", HttpStatus.INTERNAL_SERVER_ERROR, "Generic error message"), GENERIC("0", HttpStatus.INTERNAL_SERVER_ERROR, "Generic error message"),
UNAUTHORIZED("1000", HttpStatus.UNAUTHORIZED, "UNAUTHORIZED"), UNAUTHORIZED("1000", HttpStatus.UNAUTHORIZED, "UNAUTHORIZED"),
FORBIDDEN("1001", HttpStatus.FORBIDDEN, "FORBIDDEN"), 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") @JsonProperty("messageCode")
public final String messageCode; public final String messageCode;
/** A short system message that describes the cause */
@JsonProperty("systemMessage") @JsonProperty("systemMessage")
public final String systemMessage; public final String systemMessage;
/** Message details */
@JsonProperty("details") @JsonProperty("details")
public final String details; public final String details;
/** A list of additional attributes */
@JsonProperty("attributes") @JsonProperty("attributes")
public final List<String> attributes; public final List<String> attributes;
@ -137,6 +150,11 @@ public class APIMessage implements Serializable {
return this.attributes; 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) { public static final APIMessage fieldValidationError(final FieldError error) {
final String[] args = StringUtils.split(error.getDefaultMessage(), ":"); final String[] args = StringUtils.split(error.getDefaultMessage(), ":");
return ErrorMessage.FIELD_VALIDATION.of(error.toString(), args); return ErrorMessage.FIELD_VALIDATION.of(error.toString(), args);
@ -165,6 +183,10 @@ public class APIMessage implements Serializable {
return builder.toString(); 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 { public static class APIMessageException extends RuntimeException {
private static final long serialVersionUID = 1453431210820677296L; 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 { public static class FieldValidationException extends RuntimeException {
private static final long serialVersionUID = 3324566460573096815L; private static final long serialVersionUID = 3324566460573096815L;

View file

@ -46,6 +46,8 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
* @param <T> The of the result of the suppling function */ * @param <T> The of the result of the suppling function */
public final class MemoizingCircuitBreaker<T> implements Supplier<Result<T>> { public final class MemoizingCircuitBreaker<T> implements Supplier<Result<T>> {
// TODO considering invalidation time for memoizing
private static final Logger log = LoggerFactory.getLogger(MemoizingCircuitBreaker.class); private static final Logger log = LoggerFactory.getLogger(MemoizingCircuitBreaker.class);
private final CircuitBreaker<T> delegate; private final CircuitBreaker<T> delegate;

View file

@ -39,7 +39,7 @@ public enum AttributeType {
FILE_UPLOAD(BASE64_BINARY), 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), TABLE(COMPOSITE_LIST),
; ;

View file

@ -22,7 +22,7 @@ import ch.ethz.seb.sebserver.gbl.model.Domain.CONFIGURATION_VALUE;
import ch.ethz.seb.sebserver.gbl.model.GrantEntity; import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class ConfigurationTableValue implements GrantEntity { public final class ConfigurationTableValues implements GrantEntity {
public static final String ATTR_TABLE_VALUES = "tableValues"; public static final String ATTR_TABLE_VALUES = "tableValues";
@ -42,7 +42,7 @@ public final class ConfigurationTableValue implements GrantEntity {
public final List<TableValue> values; public final List<TableValue> values;
@JsonCreator @JsonCreator
public ConfigurationTableValue( public ConfigurationTableValues(
@JsonProperty(CONFIGURATION_VALUE.ATTR_INSTITUTION_ID) final Long institutionId, @JsonProperty(CONFIGURATION_VALUE.ATTR_INSTITUTION_ID) final Long institutionId,
@JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ID) final Long configurationId, @JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ID) final Long configurationId,
@JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID) final Long attributeId, @JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID) final Long attributeId,
@ -88,7 +88,7 @@ public final class ConfigurationTableValue implements GrantEntity {
@Override @Override
public String toString() { public String toString() {
return "ConfigurationTableValue [institutionId=" + this.institutionId + ", configurationId=" return "ConfigurationTableValues [institutionId=" + this.institutionId + ", configurationId="
+ this.configurationId + this.configurationId
+ ", attributeId=" + this.attributeId + ", values=" + this.values + "]"; + ", attributeId=" + this.attributeId + ", values=" + this.values + "]";
} }

View file

@ -17,7 +17,6 @@ import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.TabItem;
@ -65,15 +64,8 @@ public class FormBuilder {
this.pageContext = pageContext; this.pageContext = pageContext;
this.form = new Form(pageService.getJSONMapper()); this.form = new Form(pageService.getJSONMapper());
this.formParent = new Composite(pageContext.getParent(), SWT.NONE); this.formParent = this.widgetFactory
final GridLayout layout = new GridLayout(rows, true); .formGrid(pageContext.getParent(), rows);
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));
} }
public FormBuilder readonly(final boolean readonly) { public FormBuilder readonly(final boolean readonly) {

View file

@ -22,6 +22,10 @@ public interface InputField {
void initValue(Collection<ConfigurationValue> values); void initValue(Collection<ConfigurationValue> values);
void initValue(final String value, final Integer listIndex);
String getValue();
void showError(String errorMessage); void showError(String errorMessage);
void clearError(); void clearError();

View file

@ -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.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; 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.service.examconfig.impl.ViewContext;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
@ -24,6 +25,18 @@ public interface InputFieldBuilder {
String RES_BUNDLE_KEY_PREFIX = "sebserver.examconfig.attribute."; 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( boolean builderFor(
ConfigurationAttribute attribute, ConfigurationAttribute attribute,
Orientation orientation); Orientation orientation);

View file

@ -9,7 +9,7 @@
package ch.ethz.seb.sebserver.gui.service.examconfig; 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.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; import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext;
public interface ValueChangeListener { public interface ValueChangeListener {
@ -20,6 +20,6 @@ public interface ValueChangeListener {
String value, String value,
int listIndex); int listIndex);
void tableChanged(ConfigurationTableValue tableValue); void tableChanged(ConfigurationTableValues tableValue);
} }

View file

@ -90,12 +90,14 @@ public abstract class AbstractInputField<T extends Control> implements InputFiel
values.stream() values.stream()
.filter(a -> this.attribute.id.equals(a.attributeId)) .filter(a -> this.attribute.id.equals(a.attributeId))
.findFirst() .findFirst()
.map(v -> { .ifPresent(v -> initValue(v.value, v.listIndex));
this.initValue = v.value; }
this.listIndex = (v.listIndex != null) ? v.listIndex : 0;
@Override
public void initValue(final String value, final Integer listIndex) {
this.initValue = value;
this.listIndex = (listIndex != null) ? listIndex : 0;
setValueToControl(this.initValue); setValueToControl(this.initValue);
return this.initValue;
});
} }
protected void setDefaultValue() { protected void setDefaultValue() {

View file

@ -16,6 +16,7 @@ import org.eclipse.swt.widgets.Composite;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; 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.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
@ -66,7 +67,7 @@ public class CheckBoxBuilder implements InputFieldBuilder {
return new CheckboxField( return new CheckboxField(
attribute, attribute,
viewContext.attributeMapping.getOrientation(attribute.id), viewContext.getOrientation(attribute.id),
checkbox); checkbox);
} }
@ -84,6 +85,13 @@ public class CheckBoxBuilder implements InputFieldBuilder {
protected void setValueToControl(final String value) { protected void setValueToControl(final String value) {
this.control.setSelection(Boolean.valueOf(this.initValue)); this.control.setSelection(Boolean.valueOf(this.initValue));
} }
@Override
public String getValue() {
return this.control.getSelection()
? Constants.TRUE_STRING
: Constants.FALSE_STRING;
}
} }
} }

View file

@ -11,7 +11,6 @@ package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.tomcat.util.buf.StringUtils; 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.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration; 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.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.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.View; 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.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.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.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.service.remote.webservice.api.seb.examconfig.SaveExamConfigValue;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@ -66,20 +65,20 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
private final JSONMapper jsonMapper; private final JSONMapper jsonMapper;
private final WidgetFactory widgetFactory; private final WidgetFactory widgetFactory;
private final Collection<InputFieldBuilder> inputFieldBuilder; private final InputFieldBuilderSupplier inputFieldBuilderSupplier;
private final Collection<ValueChangeRule> valueChangeRules; private final Collection<ValueChangeRule> valueChangeRules;
protected ExamConfigurationServiceImpl( protected ExamConfigurationServiceImpl(
final RestService restService, final RestService restService,
final JSONMapper jsonMapper, final JSONMapper jsonMapper,
final WidgetFactory widgetFactory, final WidgetFactory widgetFactory,
final Collection<InputFieldBuilder> inputFieldBuilder, final InputFieldBuilderSupplier inputFieldBuilderSupplier,
final Collection<ValueChangeRule> valueChangeRules) { final Collection<ValueChangeRule> valueChangeRules) {
this.restService = restService; this.restService = restService;
this.jsonMapper = jsonMapper; this.jsonMapper = jsonMapper;
this.widgetFactory = widgetFactory; this.widgetFactory = widgetFactory;
this.inputFieldBuilder = Utils.immutableCollectionOf(inputFieldBuilder); this.inputFieldBuilderSupplier = inputFieldBuilderSupplier;
this.valueChangeRules = Utils.immutableCollectionOf(valueChangeRules); this.valueChangeRules = Utils.immutableCollectionOf(valueChangeRules);
} }
@ -93,11 +92,7 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
final ConfigurationAttribute attribute, final ConfigurationAttribute attribute,
final Orientation orientation) { final Orientation orientation) {
return this.inputFieldBuilder return this.inputFieldBuilderSupplier.getInputFieldBuilder(attribute, orientation);
.stream()
.filter(b -> b.builderFor(attribute, orientation))
.findFirst()
.orElseThrow(() -> new NoSuchElementException("No InputFieldBuilder found for : " + attribute.type));
} }
@Override @Override
@ -169,7 +164,7 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
@Override @Override
public Composite createViewGrid(final Composite parent, final ViewContext viewContext) { public Composite createViewGrid(final Composite parent, final ViewContext viewContext) {
final Composite composite = new Composite(parent, SWT.NONE); 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; gridLayout.verticalSpacing = 0;
composite.setLayout(gridLayout); composite.setLayout(gridLayout);
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
@ -179,7 +174,7 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
viewContext, viewContext,
this); this);
for (final ConfigurationAttribute attribute : viewContext.attributeMapping.getAttributes()) { for (final ConfigurationAttribute attribute : viewContext.getAttributes()) {
viewGridBuilder.add(attribute); viewGridBuilder.add(attribute);
} }
@ -269,8 +264,8 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
} }
@Override @Override
public void tableChanged(final ConfigurationTableValue tableValue) { public void tableChanged(final ConfigurationTableValues tableValue) {
this.restService.getBuilder(SaveExamConfigTableValue.class) this.restService.getBuilder(SaveExamConfigTableValues.class)
.withBody(tableValue) .withBody(tableValue)
.call(); .call();
} }

View file

@ -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> inputFieldBuilder;
protected InputFieldBuilderSupplier(final Collection<InputFieldBuilder> 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));
}
}

View file

@ -55,7 +55,7 @@ public class LabelBuilder implements InputFieldBuilder {
return new LabelField( return new LabelField(
attribute, attribute,
viewContext.attributeMapping.getOrientation(attribute.id), viewContext.getOrientation(attribute.id),
label); label);
} }
@ -74,6 +74,11 @@ public class LabelBuilder implements InputFieldBuilder {
// Does Nothing, Label has no default value // Does Nothing, Label has no default value
} }
@Override
public String getValue() {
return this.control.getText();
}
} }
} }

View file

@ -61,7 +61,7 @@ public class PassworFieldBuilder implements InputFieldBuilder {
final ConfigurationAttribute attribute, final ConfigurationAttribute attribute,
final ViewContext viewContext) { final ViewContext viewContext) {
final Orientation orientation = viewContext.attributeMapping final Orientation orientation = viewContext
.getOrientation(attribute.id); .getOrientation(attribute.id);
final Composite innerGrid = InputFieldBuilder final Composite innerGrid = InputFieldBuilder
.createInnerGrid(parent, orientation); .createInnerGrid(parent, orientation);
@ -96,15 +96,7 @@ public class PassworFieldBuilder implements InputFieldBuilder {
return; return;
} }
String hashedPWD; final String hashedPWD = passwordInputField.getValue();
try {
hashedPWD = hashPassword(pwd);
} catch (final NoSuchAlgorithmException e) {
log.error("Failed to hash password: ", e);
passwordInputField.showError("Failed to hash password");
hashedPWD = null;
}
if (hashedPWD != null) { if (hashedPWD != null) {
passwordInputField.clearError(); passwordInputField.clearError();
viewContext.getValueChangeListener().valueChanged( viewContext.getValueChangeListener().valueChanged(
@ -122,14 +114,6 @@ public class PassworFieldBuilder implements InputFieldBuilder {
return passwordInputField; 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<Text> { static final class PasswordInputField extends AbstractInputField<Text> {
private final Text confirm; 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);
}
} }
} }

View file

@ -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<ConfigurationAttribute> rowAttributes;
private final List<ConfigurationAttribute> 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<ConfigurationAttribute> getRowAttributes() {
return this.rowAttributes;
}
public List<ConfigurationAttribute> 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<Long> attributeIds) {
this.viewContext.flushInputFields(attributeIds);
}
public InputFieldBuilder getInputFieldBuilder(
final ConfigurationAttribute attribute2,
final Orientation orientation) {
return this.inputFieldBuilderSupplier.getInputFieldBuilder(attribute2, orientation);
}
public Map<Long, TableValue> 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<ConfigurationAttribute> 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<ConfigurationAttribute> 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;
}
};
}
}

View file

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * 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.ArrayList;
import java.util.Collection; 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.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; 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.ConfigurationTableValue.TableValue; 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.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; 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.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField; 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.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.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; 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.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;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; 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 static final String ROW_VALUE_KEY = "RowValues";
private final RestService restService;
private final WidgetFactory widgetFactory; 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; this.widgetFactory = widgetFactory;
} }
@Override
public void init(final InputFieldBuilderSupplier inputFieldBuilderSupplier) {
this.inputFieldBuilderSupplier = inputFieldBuilderSupplier;
}
@Override @Override
public boolean builderFor( public boolean builderFor(
final ConfigurationAttribute attribute, final ConfigurationAttribute attribute,
@ -81,31 +91,26 @@ public class TableFieldBuilder implements InputFieldBuilder {
final ViewContext viewContext) { final ViewContext viewContext) {
final I18nSupport i18nSupport = viewContext.getI18nSupport(); final I18nSupport i18nSupport = viewContext.getI18nSupport();
final TableContext tableContext = new TableContext(
final Orientation orientation = viewContext.attributeMapping this.inputFieldBuilderSupplier,
.getOrientation(attribute.id); this.widgetFactory,
final List<ConfigurationAttribute> childAttributes = this.restService,
viewContext.attributeMapping.childAttributeMapping.get(attribute.id); attribute,
final List<ConfigurationAttribute> columnAttributes = childAttributes viewContext);
.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 Table table = new Table(parent, SWT.NONE | SWT.H_SCROLL); final Table table = new Table(parent, SWT.NONE | SWT.H_SCROLL);
table.setLayout(new GridLayout()); table.setLayout(new GridLayout());
final GridData gridData = new GridData( final GridData gridData = new GridData(
SWT.FILL, SWT.FILL, SWT.FILL, SWT.FILL,
true, false, true, false,
(orientation != null) ? orientation.width() : 1, (tableContext.orientation != null) ? tableContext.orientation.width() : 1,
(orientation != null) ? orientation.height() : 1); (tableContext.orientation != null) ? tableContext.orientation.height() : 1);
gridData.heightHint = orientation.height * 40; gridData.heightHint = tableContext.orientation.height * 40;
table.setLayoutData(gridData); table.setLayoutData(gridData);
table.setHeaderVisible(true); table.setHeaderVisible(true);
table.addListener(SWT.Resize, this::adaptColumnWidth); 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 TableColumn column = new TableColumn(table, SWT.NONE);
final String text = i18nSupport.getText( final String text = i18nSupport.getText(
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + columnAttribute.name, ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + columnAttribute.name,
@ -116,13 +121,8 @@ public class TableFieldBuilder implements InputFieldBuilder {
} }
final TableInputField tableField = new TableInputField( final TableInputField tableField = new TableInputField(
this.widgetFactory, tableContext,
attribute, table);
orientation,
table,
childAttributes,
columnAttributes,
viewContext);
TableColumn column = new TableColumn(table, SWT.NONE); TableColumn column = new TableColumn(table, SWT.NONE);
column.setImage(ImageIcon.ADD_BOX.getImage(parent.getDisplay())); column.setImage(ImageIcon.ADD_BOX.getImage(parent.getDisplay()));
@ -175,31 +175,21 @@ public class TableFieldBuilder implements InputFieldBuilder {
static final class TableInputField extends AbstractInputField<Table> { static final class TableInputField extends AbstractInputField<Table> {
private final List<ConfigurationAttribute> childAttributes; private final TableContext tableContext;
private final List<ConfigurationAttribute> columnAttributes;
private final ViewContext viewContext;
private final WidgetFactory widgetFactory;
private List<Map<Long, TableValue>> values; private List<Map<Long, TableValue>> values;
TableInputField( TableInputField(
final WidgetFactory widgetFactory, final TableContext tableContext,
final ConfigurationAttribute attribute, final Table control) {
final Orientation orientation,
final Table control,
final List<ConfigurationAttribute> childAttributes,
final List<ConfigurationAttribute> columnAttributes,
final ViewContext viewContext) {
super(attribute, orientation, control, null); super(tableContext.attribute, tableContext.orientation, control, null);
this.childAttributes = childAttributes; this.tableContext = tableContext;
this.columnAttributes = columnAttributes;
this.viewContext = viewContext;
this.widgetFactory = widgetFactory;
} }
@Override @Override
public void initValue(final Collection<ConfigurationValue> values) { public void initValue(final Collection<ConfigurationValue> values) {
clearTable();
// get all child values as TableValues // get all child values as TableValues
final List<TableValue> tableValues = values.stream() final List<TableValue> tableValues = values.stream()
.filter(this::isChildValue) .filter(this::isChildValue)
@ -229,22 +219,21 @@ public class TableFieldBuilder implements InputFieldBuilder {
private boolean isChildValue(final ConfigurationValue value) { private boolean isChildValue(final ConfigurationValue value) {
return this.attribute.id.equals( return this.attribute.id.equals(
this.viewContext.attributeMapping.attributeIdMapping this.tableContext.getAttribute(value.attributeId).parentId);
.get(value.attributeId).parentId);
} }
private void deleteRow(final int selectionIndex) { private void deleteRow(final int selectionIndex) {
this.control.remove(selectionIndex); this.control.remove(selectionIndex);
this.values.remove(selectionIndex); this.values.remove(selectionIndex);
// send new values to web-service // send new values to web-service
this.viewContext.getValueChangeListener() this.tableContext.getValueChangeListener()
.tableChanged(extractTableValue()); .tableChanged(extractTableValue());
} }
private void addRow() { private void addRow() {
final int index = this.values.size(); final int index = this.values.size();
// create new values form default values // create new values form default values
final Map<Long, TableValue> rowValues = this.childAttributes final Map<Long, TableValue> rowValues = this.tableContext.getRowAttributes()
.stream() .stream()
.map(attr -> new TableValue(attr.id, index, attr.defaultValue)) .map(attr -> new TableValue(attr.id, index, attr.defaultValue))
.collect(Collectors.toMap( .collect(Collectors.toMap(
@ -254,6 +243,9 @@ public class TableFieldBuilder implements InputFieldBuilder {
this.values.add(rowValues); this.values.add(rowValues);
addTableRow(rowValues); addTableRow(rowValues);
this.control.layout(); this.control.layout();
// send new values to web-service
this.tableContext.getValueChangeListener()
.tableChanged(extractTableValue());
} }
private void addTableRow(final Map<Long, TableValue> rowValues) { private void addTableRow(final Map<Long, TableValue> rowValues) {
@ -278,88 +270,105 @@ public class TableFieldBuilder implements InputFieldBuilder {
} }
private void applyRowValues( // private void applyRowValues(
final int rowIndex, // final int rowIndex,
final Map<Long, TableValue> rowValues) { // final Map<Long, TableValue> rowValues) {
//
// set the new values // // set the new values
this.values.set(rowIndex, rowValues); // this.values.set(rowIndex, rowValues);
// update table row // // update table row
applyTableRowValues(rowIndex); // applyTableRowValues(rowIndex);
//
} // }
private void applyTableRowValues(final int index) { private void applyTableRowValues(final int index) {
final TableItem item = this.control.getItem(index); final TableItem item = this.control.getItem(index);
final Map<Long, TableValue> rowValues = this.values.get(index); final Map<Long, TableValue> rowValues = this.values.get(index);
int cellIndex = 0; int cellIndex = 0;
for (final ConfigurationAttribute attr : this.columnAttributes) { for (final ConfigurationAttribute attr : this.tableContext.getColumnAttributes()) {
final String value = rowValues.containsKey(attr.id) if (rowValues.containsKey(attr.id)) {
? rowValues.get(attr.id).value item.setText(cellIndex, rowValues.get(attr.id).value);
: null; }
item.setText(cellIndex, value);
cellIndex++; cellIndex++;
} }
item.setData(ROW_VALUE_KEY, item); item.setData(ROW_VALUE_KEY, item);
// send new values to web-service
this.viewContext.getValueChangeListener()
.tableChanged(extractTableValue());
} }
private void openForm(final int selectionIndex) { private void openForm(final int selectionIndex) {
final Map<Long, TableValue> rowValues = this.values.get(selectionIndex); final Map<Long, TableValue> rowValues = this.values.get(selectionIndex);
final ModalInputDialog<Map<Long, TableValue>> dialog = new ModalInputDialog<>( final TableRowFormBuilder builder = new TableRowFormBuilder(
this.control.getShell(), this.tableContext,
this.widgetFactory); rowValues,
selectionIndex);
final TableRowFormBuilder builder = new TableRowFormBuilder(rowValues); new ModalInputDialog<Map<Long, TableValue>>(
dialog.open( this.control.getShell(),
this.tableContext.getWidgetFactory())
.setDialogWidth(500)
.open(
new LocTextKey("Title"), new LocTextKey("Title"),
v -> System.out.println("Values Applied"), values -> applyFormValues(values, selectionIndex),
builder); builder);
} }
private ConfigurationTableValue extractTableValue() { private void applyFormValues(final Map<Long, TableValue> values, final int index) {
final Map<Long, TableValue> 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<TableValue> collect = this.values final List<TableValue> collect = this.values
.stream() .stream()
.flatMap(map -> map.values().stream()) .flatMap(map -> map.values().stream())
.collect(Collectors.toList()); .collect(Collectors.toList());
return new ConfigurationTableValue( return new ConfigurationTableValues(
this.viewContext.getInstitutionId(), this.tableContext.getInstitutionId(),
this.viewContext.getConfigurationId(), this.tableContext.getConfigurationId(),
this.attribute.id, this.attribute.id,
collect); collect);
} }
@Override @Override
protected void setDefaultValue() { protected void setDefaultValue() {
// NOTE this just empty the list for now // NOTE this just empty the list for now
// TODO do we need default values for lists? // TODO do we need default values for lists?
clearTable();
final List<TableValue> 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); this.control.setSelection(-1);
if (this.control.getItemCount() > 0) { if (this.control.getItemCount() > 0) {
for (final TableItem item : this.control.getItems()) { for (final TableItem item : this.control.getItems()) {
item.dispose(); item.dispose();
} }
} }
final List<TableValue> values = new ArrayList<>();
this.viewContext.getValueChangeListener().tableChanged(
new ConfigurationTableValue(
this.viewContext.getInstitutionId(),
this.viewContext.getConfigurationId(),
this.attribute.id,
values));
} }
@Override @Override
protected void setValueToControl(final String value) { protected void setValueToControl(final String value) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public String getValue() {
throw new UnsupportedOperationException();
}
} }
} }

View file

@ -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<Map<Long, TableValue>> {
private final TableContext tableContext;
private final Map<Long, TableValue> rowValues;
private final int listIndex;
public TableRowFormBuilder(
final TableContext tableContext,
final Map<Long, TableValue> rowValues,
final int listIndex) {
this.tableContext = tableContext;
this.rowValues = rowValues;
this.listIndex = listIndex;
}
@Override
public Supplier<Map<Long, TableValue>> compose(final Composite parent) {
final List<InputField> 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);
}
}

View file

@ -50,7 +50,7 @@ public class TextFieldBuilder implements InputFieldBuilder {
final ConfigurationAttribute attribute, final ConfigurationAttribute attribute,
final ViewContext viewContext) { final ViewContext viewContext) {
final Orientation orientation = viewContext.attributeMapping final Orientation orientation = viewContext
.getOrientation(attribute.id); .getOrientation(attribute.id);
final Composite innerGrid = InputFieldBuilder final Composite innerGrid = InputFieldBuilder
.createInnerGrid(parent, orientation); .createInnerGrid(parent, orientation);
@ -100,6 +100,11 @@ public class TextFieldBuilder implements InputFieldBuilder {
protected void setValueToControl(final String value) { protected void setValueToControl(final String value) {
this.control.setText(value); this.control.setText(value);
} }
@Override
public String getValue() {
return this.control.getText();
}
} }
} }

View file

@ -10,13 +10,13 @@ package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration; 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.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; 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.gbl.model.sebconfig.View;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField; import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.ValueChangeListener; 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 class ViewContext {
public final Configuration configuration; private final Configuration configuration;
public final View view; private final View view;
public final int columns, rows; private final int columns, rows;
public final AttributeMapping attributeMapping; private final AttributeMapping attributeMapping;
private final Map<Long, InputField> inputFieldMapping; private final Map<Long, InputField> inputFieldMapping;
private final ValueChangeListener valueChangeListener; private final ValueChangeListener valueChangeListener;
private final I18nSupport i18nSupport; private final I18nSupport i18nSupport;
@ -86,9 +86,36 @@ public final class ViewContext {
return this.rows; return this.rows;
} }
public List<ConfigurationAttribute> getChildAttributes(final ConfigurationAttribute attribute) { public Configuration getConfiguration() {
// TODO Auto-generated method stub return this.configuration;
return null; }
public View getView() {
return this.view;
}
//
// public AttributeMapping getAttributeMapping() {
// return this.attributeMapping;
// }
public Collection<ConfigurationAttribute> getChildAttributes(final Long id) {
return this.attributeMapping.childAttributeMapping.get(id);
}
public Collection<ConfigurationAttribute> getAttributes() {
return this.attributeMapping.getAttributes();
}
public ConfigurationAttribute getAttribute(final Long attributeId) {
return this.attributeMapping.getAttribute(attributeId);
}
public Collection<Orientation> getOrientationsOfGroup(final ConfigurationAttribute attribute) {
return this.attributeMapping.getOrientationsOfGroup(attribute);
}
public Orientation getOrientation(final Long attributeId) {
return this.attributeMapping.getOrientation(attributeId);
} }
public ValueChangeListener getValueChangeListener() { public ValueChangeListener getValueChangeListener() {
@ -126,4 +153,16 @@ public final class ViewContext {
.forEach(field -> field.initValue(values)); .forEach(field -> field.initValue(values));
} }
/** Removes all registered InputFields with the given attribute ids
*
* @param values Collection of attribute ids */
void flushInputFields(final Collection<Long> values) {
if (values == null) {
return;
}
values.stream()
.forEach(attrId -> this.inputFieldMapping.remove(attrId));
}
} }

View file

@ -13,6 +13,7 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout; 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.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; 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;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
public class ViewGridBuilder { public class ViewGridBuilder {
@ -49,7 +51,7 @@ public class ViewGridBuilder {
this.examConfigurationService = examConfigurationService; this.examConfigurationService = examConfigurationService;
this.parent = parent; this.parent = parent;
this.viewContext = viewContext; this.viewContext = viewContext;
this.grid = new CellFieldBuilderAdapter[viewContext.rows][viewContext.columns]; this.grid = new CellFieldBuilderAdapter[viewContext.getRows()][viewContext.getColumns()];
this.registeredGroups = new HashSet<>(); this.registeredGroups = new HashSet<>();
} }
@ -59,7 +61,7 @@ public class ViewGridBuilder {
return this; return this;
} }
final Orientation orientation = this.viewContext.attributeMapping final Orientation orientation = this.viewContext
.getOrientation(attribute.id); .getOrientation(attribute.id);
// create group builder // create group builder
@ -101,6 +103,10 @@ public class ViewGridBuilder {
} }
case LEFT: { case LEFT: {
this.grid[ypos][xpos - 1] = labelBuilder(attribute, orientation); 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; break;
} }
case TOP: { case TOP: {
@ -179,10 +185,6 @@ public class ViewGridBuilder {
final ConfigurationAttribute attribute, final ConfigurationAttribute attribute,
final Orientation orientation) { final Orientation orientation) {
if (attribute.type == AttributeType.PASSWORD_FIELD) {
return passwordConfirmLabel(attribute, orientation);
}
return new CellFieldBuilderAdapter() { return new CellFieldBuilderAdapter() {
@Override @Override
public void createCell(final ViewGridBuilder builder) { public void createCell(final ViewGridBuilder builder) {
@ -192,6 +194,7 @@ public class ViewGridBuilder {
ViewGridBuilder.this.parent, ViewGridBuilder.this.parent,
new LocTextKey(ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name), new LocTextKey(ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name),
attribute.name); attribute.name);
label.setData(RWT.CUSTOM_VARIANT, CustomVariant.TITLE_LABEL.key);
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false); final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
switch (orientation.title) { switch (orientation.title) {
@ -211,6 +214,7 @@ public class ViewGridBuilder {
} }
} }
label.setLayoutData(gridData); label.setLayoutData(gridData);
} }
}; };
} }
@ -232,6 +236,7 @@ public class ViewGridBuilder {
label.setAlignment(SWT.LEFT); label.setAlignment(SWT.LEFT);
gridData.verticalIndent = 10; gridData.verticalIndent = 10;
label.setLayoutData(gridData); label.setLayoutData(gridData);
label.setData(RWT.CUSTOM_VARIANT, CustomVariant.TITLE_LABEL.key);
} }
}; };
} }
@ -254,7 +259,7 @@ public class ViewGridBuilder {
this.builder = builder; this.builder = builder;
this.attribute = attribute; this.attribute = attribute;
this.orientationsOfGroup = this.orientationsOfGroup =
builder.viewContext.attributeMapping.getOrientationsOfGroup(attribute); builder.viewContext.getOrientationsOfGroup(attribute);
for (final Orientation o : this.orientationsOfGroup) { for (final Orientation o : this.orientationsOfGroup) {
this.x = (this.x < o.xpos()) ? o.xpos() : this.x; this.x = (this.x < o.xpos()) ? o.xpos() : this.x;
this.x = (this.y < o.ypos()) ? o.ypos() : this.y; this.x = (this.y < o.ypos()) ? o.ypos() : this.y;
@ -291,7 +296,7 @@ public class ViewGridBuilder {
final Orientation orientation, final Orientation orientation,
final InputFieldBuilder inputFieldBuilder) { final InputFieldBuilder inputFieldBuilder) {
final ConfigurationAttribute attr = this.builder.viewContext.attributeMapping final ConfigurationAttribute attr = this.builder.viewContext
.getAttribute(orientation.attributeId); .getAttribute(orientation.attributeId);
final InputField inputField = inputFieldBuilder.createInputField( final InputField inputField = inputFieldBuilder.createInputField(

View file

@ -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<Map<Long, TableValue>> {
private final Map<Long, TableValue> rowValues;
public TableRowFormBuilder(final Map<Long, TableValue> rowValues) {
this.rowValues = rowValues;
}
@Override
public Supplier<Map<Long, TableValue>> compose(final Composite parent) {
final Label test = new Label(parent, SWT.NONE);
test.setText("TEST");
return () -> null;
}
}

View file

@ -139,13 +139,10 @@ public class I18nSupportImpl implements I18nSupport {
.toString(this.displayDateFormatter); .toString(this.displayDateFormatter);
final UserInfo userInfo = this.currentUser.get(); final UserInfo userInfo = this.currentUser.get();
if (userInfo.timeZone != null && !userInfo.timeZone.equals(DateTimeZone.UTC)) { if (userInfo != null && userInfo.timeZone != null && !userInfo.timeZone.equals(DateTimeZone.UTC)) {
if (userInfo != null && userInfo.timeZone != null) {
return dateTimeStringUTC + date return dateTimeStringUTC + date
.withZone(userInfo.timeZone) .withZone(userInfo.timeZone)
.toString(this.timeZoneFormatter); .toString(this.timeZoneFormatter);
}
return dateTimeStringUTC;
} else { } else {
return dateTimeStringUTC; return dateTimeStringUTC;
} }

View file

@ -39,6 +39,7 @@ public class ModalInputDialog<T> extends Dialog {
new LocTextKey("sebserver.overall.action.close"); new LocTextKey("sebserver.overall.action.close");
private final WidgetFactory widgetFactory; private final WidgetFactory widgetFactory;
private int dialogWidth = 400;
public ModalInputDialog( public ModalInputDialog(
final Shell parent, final Shell parent,
@ -48,6 +49,11 @@ public class ModalInputDialog<T> extends Dialog {
this.widgetFactory = widgetFactory; this.widgetFactory = widgetFactory;
} }
public ModalInputDialog<T> setDialogWidth(final int dialogWidth) {
this.dialogWidth = dialogWidth;
return this;
}
public void open( public void open(
final LocTextKey title, final LocTextKey title,
final Consumer<T> callback, final Consumer<T> callback,
@ -65,6 +71,8 @@ public class ModalInputDialog<T> extends Dialog {
main.setLayout(new GridLayout()); main.setLayout(new GridLayout());
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true); final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true);
gridData.horizontalSpan = 2; gridData.horizontalSpan = 2;
gridData.widthHint = this.dialogWidth;
main.setLayoutData(gridData); main.setLayoutData(gridData);
final Supplier<T> valueSuppier = contentComposer.compose(main); final Supplier<T> valueSuppier = contentComposer.compose(main);

View file

@ -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<Collection<ConfigurationValue>> {
protected GetExamConfigTableRowValues() {
super(new TypeKey<>(
CallType.GET_LIST,
EntityType.CONFIGURATION_VALUE,
new TypeReference<Collection<ConfigurationValue>>() {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.CONFIGURATION_VALUE_ENDPOINT + API.CONFIGURATION_TABLE_ROW_VALUE_PATH_SEGMENT);
}
}

View file

@ -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.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType; 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.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy @Lazy
@Component @Component
@GuiProfile @GuiProfile
public class SaveExamConfigTableValue extends RestCall<ConfigurationTableValue> { public class SaveExamConfigTableValues extends RestCall<ConfigurationTableValues> {
protected SaveExamConfigTableValue() { protected SaveExamConfigTableValues() {
super(new TypeKey<>( super(new TypeKey<>(
CallType.SAVE, CallType.SAVE,
EntityType.CONFIGURATION_VALUE, EntityType.CONFIGURATION_VALUE,
new TypeReference<ConfigurationTableValue>() { new TypeReference<ConfigurationTableValues>() {
}), }),
HttpMethod.PUT, HttpMethod.PUT,
MediaType.APPLICATION_JSON_UTF8, MediaType.APPLICATION_JSON_UTF8,

View file

@ -175,6 +175,19 @@ public class WidgetFactory {
return defaultPageLayout; 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) { public Button buttonLocalized(final Composite parent, final String locTextKey) {
final Button button = new Button(parent, SWT.NONE); final Button button = new Button(parent, SWT.NONE);
this.injectI18n(button, new LocTextKey(locTextKey)); this.injectI18n(button, new LocTextKey(locTextKey));

View file

@ -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.api.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.GrantEntity; 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 { public class PermissionDeniedException extends RuntimeException {
private static final long serialVersionUID = 5333137812363042580L; private static final long serialVersionUID = 5333137812363042580L;

View file

@ -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.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole; 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 { public final class SEBServerUser implements UserDetails, CredentialsContainer {
private static final long serialVersionUID = 5726250141482925769L; private static final long serialVersionUID = 5726250141482925769L;

View file

@ -108,7 +108,7 @@ public interface BulkActionSupportDAO<T extends Entity> {
return bulkAction.sources return bulkAction.sources
.stream() .stream()
.map(selectionFunction) // apply select function for each source key .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 .flatMap(Collection::stream) // Flatten stream of Collection in to one stream
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }

View file

@ -12,7 +12,7 @@ import java.util.Collection;
import java.util.Set; import java.util.Set;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; 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.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
@ -26,11 +26,17 @@ public interface ConfigurationValueDAO extends EntityDAO<ConfigurationValue, Con
"Deletion is not supported for ConfigurationValue. A ConfigurationValue get automatically deleted on deletion of a Configuration"); "Deletion is not supported for ConfigurationValue. A ConfigurationValue get automatically deleted on deletion of a Configuration");
} }
Result<ConfigurationTableValue> getTableValue( Result<ConfigurationTableValues> getTableValues(
final Long institutionId, final Long institutionId,
final Long attributeId, final Long configurationId,
final Long configurationId); final Long attributeId);
Result<ConfigurationTableValue> saveTableValue(ConfigurationTableValue value); Result<ConfigurationTableValues> saveTableValues(ConfigurationTableValues value);
Result<Collection<ConfigurationValue>> getTableRowValues(
final Long institutionId,
final Long configurationId,
final Long attributeId,
final Integer rowIndex);
} }

View file

@ -20,7 +20,7 @@ public final class DAOLoggingSupport {
public static final Logger log = LoggerFactory.getLogger(DAOLoggingSupport.class); public static final Logger log = LoggerFactory.getLogger(DAOLoggingSupport.class);
public static <T> Stream<T> logUnexpectedErrorAndSkip(final Result<T> result) { public static <T> Stream<T> logAndSkipOnError(final Result<T> result) {
return Result.skipOnError( return Result.skipOnError(
result.onError(error -> log.error("Unexpected error. Object processing is skipped: ", error))); result.onError(error -> log.error("Unexpected error. Object processing is skipped: ", error)));
} }

View file

@ -20,6 +20,7 @@ public final class ResourceNotFoundException extends RuntimeException {
private static final long serialVersionUID = 8319235723086949618L; private static final long serialVersionUID = 8319235723086949618L;
/** The entity key of the resource that was requested */
public final EntityKey entityKey; public final EntityKey entityKey;
public ResourceNotFoundException(final EntityType entityType, final String modelId) { public ResourceNotFoundException(final EntityType entityType, final String modelId) {

View file

@ -82,7 +82,7 @@ public class ConfigurationAttributeDAOImpl implements ConfigurationAttributeDAO
.execute() .execute()
.stream() .stream()
.map(ConfigurationAttributeDAOImpl::toDomainModel) .map(ConfigurationAttributeDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }
@ -105,7 +105,7 @@ public class ConfigurationAttributeDAOImpl implements ConfigurationAttributeDAO
.execute() .execute()
.stream() .stream()
.map(ConfigurationAttributeDAOImpl::toDomainModel) .map(ConfigurationAttributeDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(predicate) .filter(predicate)
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }

View file

@ -84,7 +84,7 @@ public class ConfigurationDAOImpl implements ConfigurationDAO {
.execute() .execute()
.stream() .stream()
.map(ConfigurationDAOImpl::toDomainModel) .map(ConfigurationDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }
@ -113,7 +113,7 @@ public class ConfigurationDAOImpl implements ConfigurationDAO {
.execute() .execute()
.stream() .stream()
.map(ConfigurationDAOImpl::toDomainModel) .map(ConfigurationDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(predicate) .filter(predicate)
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }

View file

@ -98,7 +98,7 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO {
.execute() .execute()
.stream() .stream()
.map(ConfigurationNodeDAOImpl::toDomainModel) .map(ConfigurationNodeDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }
@ -133,7 +133,7 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO {
.execute() .execute()
.stream() .stream()
.map(ConfigurationNodeDAOImpl::toDomainModel) .map(ConfigurationNodeDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(predicate) .filter(predicate)
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
@ -362,21 +362,18 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO {
.execute() .execute()
.stream() .stream()
.forEach(attrRec -> { .forEach(attrRec -> {
final boolean bigValue = ConfigurationValueDAOImpl.isBigValue(attrRec);
final String value = templateValues.getOrDefault( final String value = templateValues.getOrDefault(
attrRec.getId(), attrRec.getId(),
attrRec.getDefaultValue()); attrRec.getDefaultValue());
//if (StringUtils.isNoneBlank(value)) {
this.configurationValueRecordMapper.insert(new ConfigurationValueRecord( this.configurationValueRecordMapper.insert(new ConfigurationValueRecord(
null, null,
configNode.institutionId, configNode.institutionId,
config.getId(), config.getId(),
attrRec.getId(), attrRec.getId(),
0, 0,
bigValue ? null : value, value,
bigValue ? value : null)); null));
//}
}); });
return configNode; return configNode;

View file

@ -13,6 +13,7 @@ import static org.mybatis.dynamic.sql.SqlBuilder.isIn;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -27,10 +28,8 @@ import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.gbl.api.EntityType; 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.ConfigurationTableValues;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeValueType; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue;
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.ConfigurationValue; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
@ -101,7 +100,7 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
.execute() .execute()
.stream() .stream()
.map(ConfigurationValueDAOImpl::toDomainModel) .map(ConfigurationValueDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(predicate) .filter(predicate)
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
@ -116,7 +115,7 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
.execute() .execute()
.stream() .stream()
.map(ConfigurationValueDAOImpl::toDomainModel) .map(ConfigurationValueDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }
@ -133,15 +132,14 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
final String value = (data.value != null) final String value = (data.value != null)
? data.value ? data.value
: attributeRecord.getDefaultValue(); : attributeRecord.getDefaultValue();
final boolean bigValue = isBigValue(attributeRecord);
final ConfigurationValueRecord newRecord = new ConfigurationValueRecord( final ConfigurationValueRecord newRecord = new ConfigurationValueRecord(
null, null,
data.institutionId, data.institutionId,
data.configurationId, data.configurationId,
data.attributeId, data.attributeId,
data.listIndex, data.listIndex,
(bigValue) ? null : value, value,
(bigValue) ? value : null); null);
this.configurationValueRecordMapper.insert(newRecord); this.configurationValueRecordMapper.insert(newRecord);
return newRecord; return newRecord;
@ -178,15 +176,14 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
id = data.id; id = data.id;
} }
final boolean bigValue = isBigValue(attributeRecord);
final ConfigurationValueRecord newRecord = new ConfigurationValueRecord( final ConfigurationValueRecord newRecord = new ConfigurationValueRecord(
id, id,
null, null,
null, null,
null, null,
data.listIndex, data.listIndex,
(bigValue) ? null : data.value, data.value,
(bigValue) ? data.value : null); null);
this.configurationValueRecordMapper.updateByPrimaryKeySelective(newRecord); this.configurationValueRecordMapper.updateByPrimaryKeySelective(newRecord);
return this.configurationValueRecordMapper.selectByPrimaryKey(id); return this.configurationValueRecordMapper.selectByPrimaryKey(id);
@ -197,28 +194,15 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<ConfigurationTableValue> getTableValue( public Result<ConfigurationTableValues> getTableValues(
final Long institutionId, final Long institutionId,
final Long attributeId, final Long configurationId,
final Long configurationId) { final Long attributeId) {
return attributeRecordById(attributeId) return attributeRecordById(attributeId)
.map(attributeRecord -> { .flatMap(this::getAttributeMapping)
.map(attributeMapping -> {
// get all attributes of the table (columns) // get all values of the table
final List<ConfigurationAttributeRecord> columnAttributes =
this.configurationAttributeRecordMapper.selectByExample()
.where(
ConfigurationAttributeRecordDynamicSqlSupport.parentId,
isEqualTo(attributeRecord.getId()))
.build()
.execute();
final Map<Long, ConfigurationAttributeRecord> 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
final List<TableValue> values = this.configurationValueRecordMapper.selectByExample() final List<TableValue> values = this.configurationValueRecordMapper.selectByExample()
.where( .where(
ConfigurationValueRecordDynamicSqlSupport.institutionId, ConfigurationValueRecordDynamicSqlSupport.institutionId,
@ -232,10 +216,13 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
.build() .build()
.execute() .execute()
.stream() .stream()
.map(value -> getTableValue(value, attributeMapping)) .map(value -> new TableValue(
value.getConfigurationAttributeId(),
value.getListIndex(),
value.getValue()))
.collect(Collectors.toList()); .collect(Collectors.toList());
return new ConfigurationTableValue( return new ConfigurationTableValues(
institutionId, institutionId,
configurationId, configurationId,
attributeId, attributeId,
@ -243,23 +230,66 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
}); });
} }
private TableValue getTableValue( @Override
final ConfigurationValueRecord value, public Result<Collection<ConfigurationValue>> getTableRowValues(
final Map<Long, ConfigurationAttributeRecord> attributeMapping) { final Long institutionId,
final Long configurationId,
final Long attributeId,
final Integer rowIndex) {
final Long configurationAttributeId = value.getConfigurationAttributeId(); return attributeRecordById(attributeId)
final ConfigurationAttributeRecord configurationAttributeRecord = attributeMapping .flatMap(this::getAttributeMapping)
.get(configurationAttributeId); .map(attributeMapping -> {
final boolean bigValue = isBigValue(configurationAttributeRecord);
return new TableValue( if (attributeMapping == null || attributeMapping.isEmpty()) {
value.getConfigurationAttributeId(), return Collections.emptyList();
value.getListIndex(), }
bigValue ? value.getText() : value.getValue());
// 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<Map<Long, ConfigurationAttributeRecord>> getAttributeMapping(
final ConfigurationAttributeRecord attributeRecord) {
return Result.tryCatch(() -> {
final List<ConfigurationAttributeRecord> columnAttributes =
this.configurationAttributeRecordMapper.selectByExample()
.where(
ConfigurationAttributeRecordDynamicSqlSupport.parentId,
isEqualTo(attributeRecord.getId()))
.build()
.execute();
return columnAttributes
.stream()
.collect(Collectors.toMap(attr -> attr.getId(), Function.identity()));
});
} }
@Override @Override
@Transactional @Transactional
public Result<ConfigurationTableValue> saveTableValue(final ConfigurationTableValue value) { public Result<ConfigurationTableValues> saveTableValues(final ConfigurationTableValues value) {
return checkInstitutionalIntegrity(value) return checkInstitutionalIntegrity(value)
.map(this::checkFollowUpIntegrity) .map(this::checkFollowUpIntegrity)
.flatMap(val -> attributeRecordById(val.attributeId)) .flatMap(val -> attributeRecordById(val.attributeId))
@ -294,15 +324,14 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
// then add the new values // then add the new values
for (final TableValue tableValue : value.values) { for (final TableValue tableValue : value.values) {
final ConfigurationAttributeRecord columnAttr = attributeMap.get(tableValue.attributeId); final ConfigurationAttributeRecord columnAttr = attributeMap.get(tableValue.attributeId);
final boolean bigValue = isBigValue(columnAttr);
final ConfigurationValueRecord valueRecord = new ConfigurationValueRecord( final ConfigurationValueRecord valueRecord = new ConfigurationValueRecord(
null, null,
value.institutionId, value.institutionId,
value.configurationId, value.configurationId,
columnAttr.getId(), columnAttr.getId(),
tableValue.listIndex, tableValue.listIndex,
(bigValue) ? null : tableValue.value, tableValue.value,
(bigValue) ? tableValue.value : null); null);
this.configurationValueRecordMapper.insert(valueRecord); this.configurationValueRecordMapper.insert(valueRecord);
} }
@ -342,23 +371,16 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
} }
private static Result<ConfigurationValue> toDomainModel(final ConfigurationValueRecord record) { private static Result<ConfigurationValue> toDomainModel(final ConfigurationValueRecord record) {
return Result.tryCatch(() -> new ConfigurationValue( return Result.tryCatch(() -> {
return new ConfigurationValue(
record.getId(), record.getId(),
record.getInstitutionId(), record.getInstitutionId(),
record.getConfigurationId(), record.getConfigurationId(),
record.getConfigurationAttributeId(), record.getConfigurationAttributeId(),
record.getListIndex(), record.getListIndex(),
record.getValue())); record.getValue());
} });
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;
}
} }
private Result<ConfigurationValue> checkInstitutionalIntegrity(final ConfigurationValue data) { private Result<ConfigurationValue> checkInstitutionalIntegrity(final ConfigurationValue data) {
@ -371,7 +393,7 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
}); });
} }
private Result<ConfigurationTableValue> checkInstitutionalIntegrity(final ConfigurationTableValue data) { private Result<ConfigurationTableValues> checkInstitutionalIntegrity(final ConfigurationTableValues data) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
final ConfigurationRecord r = this.configurationRecordMapper.selectByPrimaryKey(data.configurationId); final ConfigurationRecord r = this.configurationRecordMapper.selectByPrimaryKey(data.configurationId);
if (r.getInstitutionId().longValue() != data.institutionId.longValue()) { 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); checkFollowUp(data.configurationId);
return data; return data;
} }

View file

@ -86,7 +86,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
.execute() .execute()
.stream() .stream()
.map(ExamConfigurationMapDAOImpl::toDomainModel) .map(ExamConfigurationMapDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }
@ -112,7 +112,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
.execute() .execute()
.stream() .stream()
.map(ExamConfigurationMapDAOImpl::toDomainModel) .map(ExamConfigurationMapDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(predicate) .filter(predicate)
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }

View file

@ -97,7 +97,7 @@ public class IndicatorDAOImpl implements IndicatorDAO {
.execute() .execute()
.stream() .stream()
.map(this::toDomainModel) .map(this::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(predicate) .filter(predicate)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
@ -113,7 +113,7 @@ public class IndicatorDAOImpl implements IndicatorDAO {
.execute() .execute()
.stream() .stream()
.map(this::toDomainModel) .map(this::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }
@ -219,7 +219,7 @@ public class IndicatorDAOImpl implements IndicatorDAO {
.execute() .execute()
.stream() .stream()
.map(this::toDomainModel) .map(this::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }

View file

@ -87,7 +87,7 @@ public class InstitutionDAOImpl implements InstitutionDAO {
return records.stream() return records.stream()
.map(InstitutionDAOImpl::toDomainModel) .map(InstitutionDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }
@ -110,7 +110,7 @@ public class InstitutionDAOImpl implements InstitutionDAO {
.execute() .execute()
.stream() .stream()
.map(InstitutionDAOImpl::toDomainModel) .map(InstitutionDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(predicate) .filter(predicate)
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
@ -244,7 +244,7 @@ public class InstitutionDAOImpl implements InstitutionDAO {
.execute() .execute()
.stream() .stream()
.map(InstitutionDAOImpl::toDomainModel) .map(InstitutionDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }

View file

@ -94,7 +94,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
return records.stream() return records.stream()
.map(this::toDomainModel) .map(this::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }
@ -125,7 +125,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
.execute() .execute()
.stream() .stream()
.map(this::toDomainModel) .map(this::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(predicate) .filter(predicate)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
@ -266,7 +266,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
.execute() .execute()
.stream() .stream()
.map(this::toDomainModel) .map(this::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }

View file

@ -72,7 +72,7 @@ public class OrientationDAOImpl implements OrientationDAO {
.execute() .execute()
.stream() .stream()
.map(OrientationDAOImpl::toDomainModel) .map(OrientationDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }
@ -98,7 +98,7 @@ public class OrientationDAOImpl implements OrientationDAO {
.execute() .execute()
.stream() .stream()
.map(OrientationDAOImpl::toDomainModel) .map(OrientationDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(predicate) .filter(predicate)
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }

View file

@ -94,7 +94,7 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
return records.stream() return records.stream()
.map(SebClientConfigDAOImpl::toDomainModel) .map(SebClientConfigDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }
@ -125,7 +125,7 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
.execute() .execute()
.stream() .stream()
.map(SebClientConfigDAOImpl::toDomainModel) .map(SebClientConfigDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(predicate) .filter(predicate)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
@ -246,7 +246,7 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
.execute() .execute()
.stream() .stream()
.map(SebClientConfigDAOImpl::toDomainModel) .map(SebClientConfigDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }

View file

@ -243,7 +243,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
.execute() .execute()
.stream() .stream()
.map(UserActivityLogDAOImpl::toDomainModel) .map(UserActivityLogDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }
@ -312,7 +312,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
.execute() .execute()
.stream() .stream()
.map(UserActivityLogDAOImpl::toDomainModel) .map(UserActivityLogDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(_predicate) .filter(_predicate)
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -329,7 +329,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
.execute() .execute()
.stream() .stream()
.map(UserActivityLogDAOImpl::toDomainModel) .map(UserActivityLogDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }

View file

@ -146,7 +146,7 @@ public class UserDAOImpl implements UserDAO {
return records.stream() return records.stream()
.map(this::toDomainModel) .map(this::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }
@ -184,7 +184,7 @@ public class UserDAOImpl implements UserDAO {
.execute() .execute()
.stream() .stream()
.map(this::toDomainModel) .map(this::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(_predicate) .filter(_predicate)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
@ -362,7 +362,7 @@ public class UserDAOImpl implements UserDAO {
.execute() .execute()
.stream() .stream()
.map(this::toDomainModel) .map(this::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }

View file

@ -69,7 +69,7 @@ public class ViewDAOImpl implements ViewDAO {
.execute() .execute()
.stream() .stream()
.map(ViewDAOImpl::toDomainModel) .map(ViewDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });
} }
@ -89,7 +89,7 @@ public class ViewDAOImpl implements ViewDAO {
.execute() .execute()
.stream() .stream()
.map(ViewDAOImpl::toDomainModel) .map(ViewDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logUnexpectedErrorAndSkip) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(predicate) .filter(predicate)
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }

View file

@ -8,13 +8,13 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig; 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; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
public interface SebExamConfigService { public interface SebExamConfigService {
void validate(ConfigurationValue value); void validate(ConfigurationValue value);
void validate(ConfigurationTableValue tableValue); void validate(ConfigurationTableValues tableValue);
} }

View file

@ -9,13 +9,14 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
import java.util.Collection; 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.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; 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.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO;
@ -27,6 +28,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigServ
@WebServiceProfile @WebServiceProfile
public class SebExamConfigServiceImpl implements SebExamConfigService { public class SebExamConfigServiceImpl implements SebExamConfigService {
private static final Logger log = LoggerFactory.getLogger(SebExamConfigServiceImpl.class);
private final ConfigurationAttributeDAO configurationAttributeDAO; private final ConfigurationAttributeDAO configurationAttributeDAO;
private final Collection<ConfigurationValueValidator> validators; private final Collection<ConfigurationValueValidator> validators;
@ -40,7 +43,10 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
@Override @Override
public void validate(final ConfigurationValue value) { 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) final ConfigurationAttribute attribute = this.configurationAttributeDAO.byPK(value.attributeId)
.getOrThrow(); .getOrThrow();
@ -53,7 +59,7 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
} }
@Override @Override
public void validate(final ConfigurationTableValue tableValue) { public void validate(final ConfigurationTableValues tableValue) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
} }

View file

@ -8,19 +8,21 @@
package ch.ethz.seb.sebserver.webservice.weblayer.api; 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; private static final long serialVersionUID = 3732727447520974727L;
public IllegalAPIArgumentException() { public APIConstraintViolationException() {
super(); super();
} }
public IllegalAPIArgumentException(final String message, final Throwable cause) { public APIConstraintViolationException(final String message, final Throwable cause) {
super(message, cause); super(message, cause);
} }
public IllegalAPIArgumentException(final String message) { public APIConstraintViolationException(final String message) {
super(message); super(message);
} }

View file

@ -111,9 +111,9 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler {
.createErrorResponse(ex.getMessage()); .createErrorResponse(ex.getMessage());
} }
@ExceptionHandler(IllegalAPIArgumentException.class) @ExceptionHandler(APIConstraintViolationException.class)
public ResponseEntity<Object> handleIllegalAPIArgumentException( public ResponseEntity<Object> handleIllegalAPIArgumentException(
final IllegalAPIArgumentException ex, final APIConstraintViolationException ex,
final WebRequest request) { final WebRequest request) {
log.warn("Illegal API Argument Exception: ", ex); log.warn("Illegal API Argument Exception: ", ex);

View file

@ -8,6 +8,7 @@
package ch.ethz.seb.sebserver.webservice.weblayer.api; package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.util.Collection;
import java.util.Objects; import java.util.Objects;
import javax.validation.Valid; 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.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; 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.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
@ -90,7 +91,7 @@ public class ConfigurationValueController extends EntityController<Configuration
method = RequestMethod.GET, method = RequestMethod.GET,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE) produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ConfigurationTableValue getTableValueBy( public ConfigurationTableValues getTableValueBy(
@RequestParam( @RequestParam(
name = Domain.CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID, name = Domain.CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID,
required = true) final Long attributeId, required = true) final Long attributeId,
@ -100,10 +101,36 @@ public class ConfigurationValueController extends EntityController<Configuration
return this.configurationDAO.byPK(configurationId) return this.configurationDAO.byPK(configurationId)
.flatMap(this.authorization::checkRead) .flatMap(this.authorization::checkRead)
.flatMap(config -> this.configurationValueDAO.getTableValue( .flatMap(config -> this.configurationValueDAO.getTableValues(
config.institutionId, 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<ConfigurationValue> 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, attributeId,
configurationId)) rowIndex))
.getOrThrow(); .getOrThrow();
} }
@ -112,12 +139,12 @@ public class ConfigurationValueController extends EntityController<Configuration
method = RequestMethod.PUT, method = RequestMethod.PUT,
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE) produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ConfigurationTableValue savePut( public ConfigurationTableValues savePut(
@Valid @RequestBody final ConfigurationTableValue tableValue) { @Valid @RequestBody final ConfigurationTableValues tableValue) {
return this.configurationDAO.byPK(tableValue.configurationId) return this.configurationDAO.byPK(tableValue.configurationId)
.flatMap(this.authorization::checkModify) .flatMap(this.authorization::checkModify)
.flatMap(config -> this.configurationValueDAO.saveTableValue(tableValue)) .flatMap(config -> this.configurationValueDAO.saveTableValues(tableValue))
.getOrThrow(); .getOrThrow();
} }
@ -149,7 +176,7 @@ public class ConfigurationValueController extends EntityController<Configuration
_entity.attributeId != null && _entity.attributeId != null &&
_entity.listIndex != null; _entity.listIndex != null;
if (!idSet && !idsSet) { if (!idSet && !idsSet) {
throw new IllegalAPIArgumentException( throw new APIConstraintViolationException(
"Missing some mandatory attributes. Either id must be set or all of configurationId, attributeId and listIndex"); "Missing some mandatory attributes. Either id must be set or all of configurationId, attributeId and listIndex");
} }

View file

@ -355,7 +355,7 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
return this.beanValidationService.validateBean(entity); return this.beanValidationService.validateBean(entity);
} else { } else {
return Result.ofError( 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<T extends Entity, M extends Entity> {
if (entity.getModelId() != null) { if (entity.getModelId() != null) {
return Result.of(entity); return Result.of(entity);
} else { } else {
return Result.ofError(new IllegalAPIArgumentException("Missing model identifier")); return Result.ofError(new APIConstraintViolationException("Missing model identifier"));
} }
} }

View file

@ -98,7 +98,7 @@ public class SebClientConfigController extends ActivatableEntityController<SebCl
Domain.SEB_CLIENT_CONFIGURATION.ATTR_INSTITUTION_ID); Domain.SEB_CLIENT_CONFIGURATION.ATTR_INSTITUTION_ID);
if (institutionId == null) { if (institutionId == null) {
throw new IllegalAPIArgumentException("Institution identifier is missing"); throw new APIConstraintViolationException("Institution identifier is missing");
} }
postParams.putIfAbsent( postParams.putIfAbsent(

View file

@ -156,7 +156,7 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
// check of institution of UserInfo is active. Otherwise save is not valid // check of institution of UserInfo is active. Otherwise save is not valid
if (!this.beanValidationService if (!this.beanValidationService
.isActive(new EntityKey(userInfo.getInstitutionId(), EntityType.INSTITUTION))) { .isActive(new EntityKey(userInfo.getInstitutionId(), EntityType.INSTITUTION))) {
throw new IllegalAPIArgumentException( throw new APIConstraintViolationException(
"User within an inactive institution cannot be created nor modified"); "User within an inactive institution cannot be created nor modified");
} }
@ -168,7 +168,7 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
if (userRolesOfAccount.contains(UserRole.SEB_SERVER_ADMIN) && if (userRolesOfAccount.contains(UserRole.SEB_SERVER_ADMIN) &&
!rolesOfCurrentUser.contains(UserRole.SEB_SERVER_ADMIN)) { !rolesOfCurrentUser.contains(UserRole.SEB_SERVER_ADMIN)) {
throw new IllegalAPIArgumentException( throw new APIConstraintViolationException(
"The current user cannot edit a User-Account of heigher role pased rank: " "The current user cannot edit a User-Account of heigher role pased rank: "
+ UserRole.SEB_SERVER_ADMIN); + UserRole.SEB_SERVER_ADMIN);
} }
@ -182,7 +182,7 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
.orElse(null); .orElse(null);
if (nonePublicRole != null) { if (nonePublicRole != null) {
throw new IllegalAPIArgumentException( throw new APIConstraintViolationException(
"The current user has not the privilege to create a User-Account with none public role: " "The current user has not the privilege to create a User-Account with none public role: "
+ nonePublicRole); + nonePublicRole);
} }

View file

@ -1,32 +0,0 @@
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>sebMode</key>
<integer>1</integer>
<key>sebServerFallback</key>
<true />
<key>sebServerURL</key>
<string>http://localhost:8080</string>
<key>sebServerConfiguration</key>
<array>
<dict>
<key>institution</key>
<string>1</string>
<key>clientName</key>
<string>+Ra01]`-4e*$(=@=</string>
<key>clientSecret</key>
<string>(+8Jl-#l8t(XHi%hxi88HXsjC{BE1F(TgdbQSzz=TpR]%_fJ~IjkdAL^YzfV*()t</string>
<key>accessTokenEndpoint</key>
<string>/oauth/token</string>
<key>handshakeEndpoint</key>
<string>/exam-api/v1/handshake</string>
<key>examConfigEndpoint</key>
<string>/exam-api/v1/examconfig</string>
<key>pingEndpoint</key>
<string>/exam-api/v1/sebping</string>
<key>eventEndpoint</key>
<string>/exam-api/v1/sebevent</string>
</dict>
</array>
</dict>
</plist>