SEBSERV-72 implementation and config fixes

This commit is contained in:
anhefti 2019-10-17 16:53:33 +02:00
parent c96901472b
commit 2f29f14dc2
24 changed files with 606 additions and 135 deletions

View file

@ -11,7 +11,6 @@ package ch.ethz.seb.sebserver;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
@ -39,7 +38,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ResourceUtils;
@ -95,11 +93,18 @@ public class ClientHttpRequestFactoryService {
final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setHttpClient(this.createProxiedClient(proxy, null));
factory.setBufferRequestBody(false);
return factory;
} else {
final DevClientHttpRequestFactory devClientHttpRequestFactory = new DevClientHttpRequestFactory();
devClientHttpRequestFactory.setOutputStreaming(false);
final HttpComponentsClientHttpRequestFactory devClientHttpRequestFactory =
new HttpComponentsClientHttpRequestFactory();
devClientHttpRequestFactory.setBufferRequestBody(false);
// final HttpClient httpClient = devClientHttpRequestFactory.getHttpClient();
// httpClient.setInstanceFollowRedirects(false);
// httpClient.setOutputStreaming(false);
return devClientHttpRequestFactory;
}
}
@ -204,17 +209,17 @@ public class ClientHttpRequestFactoryService {
}
// TODO set connection and read timeout!? configurable!?
private static class DevClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
@Override
protected void prepareConnection(
final HttpURLConnection connection,
final String httpMethod) throws IOException {
super.prepareConnection(connection, httpMethod);
super.setBufferRequestBody(false);
connection.setInstanceFollowRedirects(false);
}
}
// private static class DevClientHttpRequestFactory extends HttpComponentsClientHttpRequestFactory {
//
// @Override
// protected void prepareConnection(
// final HttpURLConnection connection,
// final String httpMethod) throws IOException {
//
// super.prepareConnection(connection, httpMethod);
// super.setBufferRequestBody(false);
// connection.setInstanceFollowRedirects(false);
// }
// }
}

View file

@ -27,6 +27,7 @@ public final class API {
public static final String PARAM_ENTITY_TYPE = "entityType";
public static final String PARAM_BULK_ACTION_TYPE = "bulkActionType";
public static final String PARAM_CLIENT_CONFIG_SECRET = "client_config_secret";
public static final String PARAM_VIEW_ID = "viewId";
public static final String DEFAULT_CONFIG_TEMPLATE_ID = String.valueOf(ConfigurationNode.DEFAULT_TEMPLATE_ID);
public static final String INSTITUTION_VAR_PATH_SEGMENT = "/{" + PARAM_INSTITUTION_ID + "}";
@ -128,6 +129,9 @@ public final class API {
public static final String IMPORT_FILE_ATTR_NAME = "importFile";
public static final String TEMPLATE_ATTRIBUTE_ENDPOINT = "/template-attribute";
public static final String TEMPLATE_ATTRIBUTE_RESET_VALUES = "/reset";
public static final String TEMPLATE_ATTRIBUTE_REMOVE_ORIENTATION = "remove-orientation";
public static final String TEMPLATE_ATTRIBUTE_ATTACH_DEFAUL_ORIENTATION = "attach-default-orientation";
public static final String ORIENTATION_ENDPOINT = "/orientation";
public static final String VIEW_ENDPOINT = ORIENTATION_ENDPOINT + "/view";

View file

@ -23,6 +23,7 @@ import ch.ethz.seb.sebserver.gbl.model.Entity;
@JsonIgnoreProperties(ignoreUnknown = true)
public final class Orientation implements Entity {
public static final String FILTER_ATTR_ATTRIBUTE_ID = "attributeId";
public static final String FILTER_ATTR_TEMPLATE_ID = "templateId";
public static final String FILTER_ATTR_VIEW_ID = "viewId";
public static final String FILTER_ATTR_GROUP_ID = "groupId";

View file

@ -10,6 +10,9 @@ package ch.ethz.seb.sebserver.gui.content;
import java.util.Arrays;
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.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@ -36,13 +39,13 @@ import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurations;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetTemplateAttribute;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
@Lazy
@Component
@ -142,10 +145,17 @@ public class ConfigTemplateAttributeForm implements TemplateComposer {
attribute.getGroupId()))
.build();
widgetFactory.labelLocalizedTitle(
widgetFactory.labelLocalized(
content,
CustomVariant.TEXT_H2,
FORM_VALUE_TEXT_KEY);
final Composite grid = new Composite(content, SWT.NONE);
grid.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
grid.setLayout(new GridLayout(6, true));
final PageContext valueContext = formContext.copyOf(grid);
final InputFieldBuilder inputFieldBuilder = this.examConfigurationService.getInputFieldBuilder(
attribute.getConfigAttribute(),
attribute.getOrientation());
@ -154,7 +164,7 @@ public class ConfigTemplateAttributeForm implements TemplateComposer {
.getOrThrow();
final ViewContext viewContext = this.examConfigurationService.createViewContext(
formContext,
valueContext,
configuration,
new View(-1L, "template", 10, 0, templateId),
attributeMapping,
@ -171,9 +181,7 @@ public class ConfigTemplateAttributeForm implements TemplateComposer {
configuration.id,
Arrays.asList(viewContext));
final PageActionBuilder pageActionBuilder = this.pageService
.pageActionBuilder(formContext.clearEntityKeys());
pageActionBuilder
this.pageService.pageActionBuilder(formContext.clearEntityKeys())
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_FORM_SET_DEFAULT)
.withEntityKey(attributeKey)

View file

@ -282,7 +282,7 @@ public class ConfigTemplateForm implements TemplateComposer {
final PageAction action,
final EntityTable<TemplateAttribute> attrTable) {
final PageAction removeFormView = this.examConfigurationService.removeFormView(action);
final PageAction removeFormView = this.examConfigurationService.removeFromView(action);
// reload the list
attrTable.applyFilter();
return removeFormView;

View file

@ -61,7 +61,9 @@ public interface ExamConfigurationService {
PageAction resetToDefaults(PageAction action);
PageAction removeFormView(PageAction action);
PageAction removeFromView(PageAction action);
PageAction attachToDefaultView(final PageAction action);
static String attributeNameKey(final ConfigurationAttribute attribute) {
if (attribute == null) {

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.tomcat.util.buf.StringUtils;
@ -46,12 +47,16 @@ import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.FieldValidationError;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.AttchDefaultOrientation;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigAttributes;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurationValues;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetOrientations;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetViewList;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.RemoveOrientation;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ResetTemplateValues;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigTableValues;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigValue;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@ -213,21 +218,82 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
@Override
public final PageAction resetToDefaults(final PageAction action) {
final EntityKey singleSelection = action.getSingleSelection();
final EntityKey parentEntityKey = action.pageContext().getParentEntityKey();
final Set<EntityKey> selection = action.getMultiSelection();
if (selection != null && !selection.isEmpty()) {
selection.stream().forEach(entityKey -> {
callTemplateAction(
ResetTemplateValues.class,
parentEntityKey.modelId,
entityKey.modelId);
});
} else {
final EntityKey entityKey = action.getEntityKey();
callTemplateAction(
ResetTemplateValues.class,
parentEntityKey.modelId,
entityKey.modelId);
}
// TODO
return action;
}
@Override
public final PageAction removeFormView(final PageAction action) {
final EntityKey singleSelection = action.getSingleSelection();
// TODO
public final PageAction removeFromView(final PageAction action) {
final EntityKey parentEntityKey = action.pageContext().getParentEntityKey();
final Set<EntityKey> selection = action.getMultiSelection();
if (selection != null && !selection.isEmpty()) {
selection.stream().forEach(entityKey -> {
callTemplateAction(
RemoveOrientation.class,
parentEntityKey.modelId,
entityKey.modelId);
});
} else {
final EntityKey entityKey = action.getEntityKey();
callTemplateAction(
RemoveOrientation.class,
parentEntityKey.modelId,
entityKey.modelId);
}
return action;
}
@Override
public final PageAction attachToDefaultView(final PageAction action) {
final EntityKey parentEntityKey = action.pageContext().getParentEntityKey();
final Set<EntityKey> selection = action.getMultiSelection();
if (selection != null && !selection.isEmpty()) {
selection.stream().forEach(entityKey -> {
callTemplateAction(
AttchDefaultOrientation.class,
parentEntityKey.modelId,
entityKey.modelId);
});
} else {
final EntityKey entityKey = action.getEntityKey();
callTemplateAction(
AttchDefaultOrientation.class,
parentEntityKey.modelId,
entityKey.modelId);
}
return action;
}
private void callTemplateAction(
final Class<? extends RestCall<?>> actionType,
final String templateId,
final String attributeId) {
this.restService.getBuilder(actionType)
.withURIVariable(API.PARAM_PARENT_MODEL_ID, templateId)
.withURIVariable(API.PARAM_MODEL_ID, attributeId)
.call()
.getOrThrow();
}
private static final class ValueChangeListenerImpl implements ValueChangeListener {
public static final String VALIDATION_ERROR_KEY_PREFIX = "sebserver.examconfig.props.validation.";

View file

@ -0,0 +1,44 @@
/*
* 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 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.TemplateAttribute;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class AttchDefaultOrientation extends RestCall<TemplateAttribute> {
public AttchDefaultOrientation() {
super(new TypeKey<>(
CallType.SAVE,
EntityType.CONFIGURATION_NODE,
new TypeReference<TemplateAttribute>() {
}),
HttpMethod.PATCH,
MediaType.APPLICATION_FORM_URLENCODED,
API.CONFIGURATION_NODE_ENDPOINT
+ API.PARENT_MODEL_ID_VAR_PATH_SEGMENT
+ API.TEMPLATE_ATTRIBUTE_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT
+ API.TEMPLATE_ATTRIBUTE_ATTACH_DEFAUL_ORIENTATION);
}
}

View file

@ -0,0 +1,44 @@
/*
* 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 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.TemplateAttribute;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class RemoveOrientation extends RestCall<TemplateAttribute> {
public RemoveOrientation() {
super(new TypeKey<>(
CallType.SAVE,
EntityType.CONFIGURATION_NODE,
new TypeReference<TemplateAttribute>() {
}),
HttpMethod.PATCH,
MediaType.APPLICATION_FORM_URLENCODED,
API.CONFIGURATION_NODE_ENDPOINT
+ API.PARENT_MODEL_ID_VAR_PATH_SEGMENT
+ API.TEMPLATE_ATTRIBUTE_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT
+ API.TEMPLATE_ATTRIBUTE_REMOVE_ORIENTATION);
}
}

View file

@ -0,0 +1,44 @@
/*
* 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 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.TemplateAttribute;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class ResetTemplateValues extends RestCall<TemplateAttribute> {
public ResetTemplateValues() {
super(new TypeKey<>(
CallType.SAVE,
EntityType.CONFIGURATION_NODE,
new TypeReference<TemplateAttribute>() {
}),
HttpMethod.PATCH,
MediaType.APPLICATION_FORM_URLENCODED,
API.CONFIGURATION_NODE_ENDPOINT
+ API.PARENT_MODEL_ID_VAR_PATH_SEGMENT
+ API.TEMPLATE_ATTRIBUTE_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT
+ API.TEMPLATE_ATTRIBUTE_RESET_VALUES);
}
}

View file

@ -36,6 +36,7 @@ public class WebserviceInfo {
private static final String WEB_SERVICE_TEST_PROPERTY = "sebserver.test.property";
private static final String WEB_SERVICE_SERVER_NAME_KEY = "sebserver.webservice.http.server.name";
private static final String WEB_SERVICE_HTTP_SCHEME_KEY = "sebserver.webservice.http.scheme";
private static final String WEB_SERVICE_HTTP_PORT = "sebserver.webservice.http.port";
private static final String WEB_SERVICE_HOST_ADDRESS_KEY = "server.address";
private static final String WEB_SERVICE_SERVER_PORT_KEY = "server.port";
private static final String WEB_SERVICE_EXAM_API_DISCOVERY_ENDPOINT_KEY =
@ -45,8 +46,9 @@ public class WebserviceInfo {
private final String testProperty;
private final String httpScheme;
private final String hostAddress; // internal
private final String serverName; // external
private final String serverPort;
private final String webserverName; // external
private final String serverPort; // internal
private final String webserverPort; // external
private final String discoveryEndpoint;
private final String serverURLPrefix;
@ -58,17 +60,21 @@ public class WebserviceInfo {
this.testProperty = environment.getProperty(WEB_SERVICE_TEST_PROPERTY, "NOT_AVAILABLE");
this.httpScheme = environment.getRequiredProperty(WEB_SERVICE_HTTP_SCHEME_KEY);
this.hostAddress = environment.getRequiredProperty(WEB_SERVICE_HOST_ADDRESS_KEY);
this.serverName = environment.getProperty(WEB_SERVICE_SERVER_NAME_KEY, "");
this.webserverName = environment.getProperty(WEB_SERVICE_SERVER_NAME_KEY, "");
this.serverPort = environment.getRequiredProperty(WEB_SERVICE_SERVER_PORT_KEY);
this.webserverPort = environment.getProperty(WEB_SERVICE_HTTP_PORT);
this.discoveryEndpoint = environment.getRequiredProperty(WEB_SERVICE_EXAM_API_DISCOVERY_ENDPOINT_KEY);
this.serverURLPrefix = UriComponentsBuilder.newInstance()
final UriComponentsBuilder builder = UriComponentsBuilder.newInstance()
.scheme(this.httpScheme)
.host((StringUtils.isNotBlank(this.serverName))
? this.serverName
: this.hostAddress)
.port(this.serverPort)
.toUriString();
.host((StringUtils.isNotBlank(this.webserverName))
? this.webserverName
: this.hostAddress);
if (StringUtils.isNotBlank(this.webserverPort)) {
builder.port(this.webserverPort);
}
this.serverURLPrefix = builder.toUriString();
this.isDistributed = BooleanUtils.toBoolean(environment.getProperty(
"sebserver.webservice.distributed",
@ -106,14 +112,18 @@ public class WebserviceInfo {
return this.hostAddress;
}
public String getServerName() {
return this.serverName;
public String getWebserviceDomainName() {
return this.webserverName;
}
public String getServerPort() {
return this.serverPort;
}
public String getServerExternalPort() {
return this.webserverPort;
}
public String getDiscoveryEndpoint() {
return this.discoveryEndpoint;
}
@ -183,18 +193,20 @@ public class WebserviceInfo {
builder.append(this.httpScheme);
builder.append(", hostAddress=");
builder.append(this.hostAddress);
builder.append(", serverName=");
builder.append(this.serverName);
builder.append(", webserverName=");
builder.append(this.webserverName);
builder.append(", serverPort=");
builder.append(this.serverPort);
builder.append(", webserverPort=");
builder.append(this.webserverPort);
builder.append(", discoveryEndpoint=");
builder.append(this.discoveryEndpoint);
builder.append(", externalAddressAlias=");
builder.append(this.externalAddressAlias);
builder.append(", serverURLPrefix=");
builder.append(this.serverURLPrefix);
builder.append(", isDistributed=");
builder.append(this.isDistributed);
builder.append(", externalAddressAlias=");
builder.append(this.externalAddressAlias);
builder.append("]");
return builder.toString();
}

View file

@ -8,6 +8,8 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
import java.util.Arrays;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.util.LinkedMultiValueMap;
@ -119,6 +121,10 @@ public class FilterMap extends POSTMapper {
return getLong(Orientation.FILTER_ATTR_TEMPLATE_ID);
}
public Long getOrientationAttributeId() {
return getLong(Orientation.FILTER_ATTR_ATTRIBUTE_ID);
}
public Long getOrientationViewId() {
return getLong(Orientation.FILTER_ATTR_VIEW_ID);
}
@ -284,4 +290,23 @@ public class FilterMap extends POSTMapper {
return null;
}
public static final class Builder {
private final FilterMap filterMap = new FilterMap();
public Builder add(final String name, final String value) {
this.filterMap.params.add(name, value);
return this;
}
public Builder put(final String name, final String value) {
this.filterMap.params.put(name, Arrays.asList(value));
return this;
}
public FilterMap create() {
return new FilterMap(this.filterMap.params);
}
}
}

View file

@ -8,6 +8,7 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
import java.util.List;
import java.util.Map;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
@ -18,4 +19,8 @@ public interface ViewDAO extends EntityDAO<View, View> {
Result<Map<Long, Long>> copyDefaultViewsForTemplate(ConfigurationNode node);
Result<List<View>> getDefaultTemplateViews();
Result<View> getDefaultViewForTemplate(Long templateId, Long defaultViewId);
}

View file

@ -91,6 +91,9 @@ public class OrientationDAOImpl implements OrientationDAO {
.where(
OrientationRecordDynamicSqlSupport.templateId,
SqlBuilder.isEqualToWhenPresent(filterMap.getOrientationTemplateId()))
.and(
OrientationRecordDynamicSqlSupport.configAttributeId,
SqlBuilder.isEqualToWhenPresent(filterMap.getOrientationAttributeId()))
.and(
OrientationRecordDynamicSqlSupport.viewId,
SqlBuilder.isEqualToWhenPresent(filterMap.getOrientationViewId()))

View file

@ -100,6 +100,47 @@ public class ViewDAOImpl implements ViewDAO {
.collect(Collectors.toList()));
}
@Override
@Transactional(readOnly = true)
public Result<List<View>> getDefaultTemplateViews() {
return Result.tryCatch(() -> this.viewRecordMapper
.selectByExample()
.where(
ViewRecordDynamicSqlSupport.templateId,
SqlBuilder.isEqualTo(ConfigurationNode.DEFAULT_TEMPLATE_ID))
.build()
.execute()
.stream()
.map(ViewDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList()));
}
@Override
@Transactional(readOnly = true)
public Result<View> getDefaultViewForTemplate(final Long templateId, final Long defaultViewId) {
return Result.tryCatch(() -> {
// get all views of template
final List<ViewRecord> templateViews = this.viewRecordMapper
.selectByExample()
.where(
ViewRecordDynamicSqlSupport.templateId,
SqlBuilder.isEqualTo(templateId))
.build()
.execute();
// get default view
final ViewRecord defView = this.viewRecordMapper.selectByPrimaryKey(defaultViewId);
final ViewRecord result = templateViews
.stream()
.filter(view -> view.getName().equals(defView.getName()))
.findFirst()
.orElseThrow();
return result;
})
.flatMap(ViewDAOImpl::toDomainModel);
}
@Override
@Transactional
public Result<View> createNew(final View data) {

View file

@ -9,9 +9,7 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig;
import java.util.List;
import java.util.Set;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
@ -29,9 +27,20 @@ public interface SebExamConfigTemplateService {
final Long templateId,
final Long attributeId);
Result<Set<EntityKey>> setDefaultValues(
Result<TemplateAttribute> setDefaultValues(
final Long institutionId,
final Long templateId,
final Long attributeId);
Result<TemplateAttribute> removeOrientation(
final Long institutionId,
final Long templateId,
final Long attributeId);
Result<TemplateAttribute> attachDefaultOrientation(
final Long institutionId,
final Long templateId,
final Long attributeId,
Long viewId);
}

View file

@ -8,33 +8,34 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.PageSortOrder;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationValueDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.OrientationDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ViewDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigService;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigTemplateService;
@Lazy
@ -42,28 +43,26 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigTemp
@WebServiceProfile
public class SebExamConfigTemplateServiceImpl implements SebExamConfigTemplateService {
private final ConfigurationNodeDAO ConfigurationNodeDAO;
private final ConfigurationDAO configurationDAO;
private static final Logger log = LoggerFactory.getLogger(SebExamConfigTemplateServiceImpl.class);
private final ViewDAO viewDAO;
private final ConfigurationDAO configurationDAO;
private final OrientationDAO orientationDAO;
private final ConfigurationAttributeDAO configurationAttributeDAO;
private final ConfigurationValueDAO configurationValueDAO;
private final SebExamConfigService sebExamConfigService;
protected SebExamConfigTemplateServiceImpl(
final ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO configurationNodeDAO,
final ConfigurationDAO configurationDAO, final ViewDAO viewDAO, final OrientationDAO orientationDAO,
final ViewDAO viewDAO,
final ConfigurationDAO configurationDAO,
final OrientationDAO orientationDAO,
final ConfigurationAttributeDAO configurationAttributeDAO,
final ConfigurationValueDAO configurationValueDAO,
final SebExamConfigService sebExamConfigService) {
super();
this.ConfigurationNodeDAO = configurationNodeDAO;
this.configurationDAO = configurationDAO;
final ConfigurationValueDAO configurationValueDAO) {
this.viewDAO = viewDAO;
this.configurationDAO = configurationDAO;
this.orientationDAO = orientationDAO;
this.configurationAttributeDAO = configurationAttributeDAO;
this.configurationValueDAO = configurationValueDAO;
this.sebExamConfigService = sebExamConfigService;
}
@Override
@ -127,7 +126,7 @@ public class SebExamConfigTemplateServiceImpl implements SebExamConfigTemplateSe
}
@Override
public Result<Set<EntityKey>> setDefaultValues(
public Result<TemplateAttribute> setDefaultValues(
final Long institutionId,
final Long templateId,
final Long attributeId) {
@ -137,7 +136,102 @@ public class SebExamConfigTemplateServiceImpl implements SebExamConfigTemplateSe
.setDefaultValues(
institutionId,
config.id,
attributeId));
attributeId))
.flatMap(vals -> getAttribute(
institutionId,
templateId,
attributeId));
}
@Override
public Result<TemplateAttribute> removeOrientation(
final Long institutionId,
final Long templateId,
final Long attributeId) {
return Result.tryCatch(() -> {
final Orientation orientation = getOrientation(templateId, attributeId);
this.orientationDAO.delete(new HashSet<>(Arrays.asList(orientation.getEntityKey())))
.getOrThrow();
final TemplateAttribute attribute = getAttribute(institutionId, templateId, attributeId)
.getOrThrow();
if (attribute.getOrientation() != null) {
throw new IllegalStateException(
"Failed to remove Orientation, expecting no Orientatoin for attribute: " + attribute);
}
return attribute;
});
}
@Override
public Result<TemplateAttribute> attachDefaultOrientation(
final Long institutionId,
final Long templateId,
final Long attributeId,
final Long viewId) {
return Result.tryCatch(() -> {
final Orientation orientation = getOrientation(templateId, attributeId);
final Orientation devOrientation = getOrientation(ConfigurationNode.DEFAULT_TEMPLATE_ID, attributeId);
if (orientation != null) {
this.orientationDAO.delete(new HashSet<>(Arrays.asList(orientation.getEntityKey())))
.getOrThrow();
}
final Long _viewId;
if (viewId == null) {
_viewId = this.viewDAO.getDefaultViewForTemplate(templateId, devOrientation.viewId)
.getOrThrow().id;
} else {
_viewId = viewId;
}
final Orientation newOrientation = new Orientation(
null,
attributeId,
templateId,
_viewId,
devOrientation.groupId,
devOrientation.xPosition,
devOrientation.yPosition,
devOrientation.width,
devOrientation.height,
devOrientation.title);
this.orientationDAO.save(newOrientation)
.getOrThrow();
final TemplateAttribute attribute = getAttribute(institutionId, templateId, attributeId)
.getOrThrow();
if (attribute.getOrientation() == null) {
throw new IllegalStateException(
"Failed to attach default Orientation, expecting Orientatoin for attribute: " + attribute);
}
return attribute;
});
}
private Orientation getOrientation(final Long templateId, final Long attributeId) {
final FilterMap filterMap = new FilterMap.Builder()
.put(Orientation.FILTER_ATTR_TEMPLATE_ID, String.valueOf(templateId))
.put(Orientation.FILTER_ATTR_ATTRIBUTE_ID, String.valueOf(attributeId))
.create();
return this.orientationDAO.allMatching(filterMap)
.get(error -> {
log.warn("Unexpecrted error while get Orientation: ", error);
return Collections.emptyList();
})
.stream()
.findFirst()
.orElse(null);
}
}

View file

@ -14,6 +14,7 @@ import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.logging.LogLevel;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@ -31,6 +32,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.PingHandlingStrategy;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SebClientConnectionService;
import ch.ethz.seb.sebserver.webservice.weblayer.api.APIConstraintViolationException;
import ch.ethz.seb.sebserver.webservice.weblayer.api.OnlyMessageLogExceptionWrapper;
@Lazy
@Service
@ -392,7 +394,9 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
if (activeClientConnection != null) {
if (activeClientConnection.clientConnection.status != ConnectionStatus.ESTABLISHED) {
throw new IllegalStateException("No established SEB client connection");
throw new OnlyMessageLogExceptionWrapper(
new IllegalStateException("No established SEB client connection"),
LogLevel.WARN);
}
// store event
@ -404,6 +408,8 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
activeClientConnection.getindicatorMapping(event.eventType)
.stream()
.forEach(indicator -> indicator.notifyValueChange(event));
} else {
log.warn("No active ClientConnection found for connectionToken: {}", connectionToken);
}
}

View file

@ -53,6 +53,9 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler {
if (ex instanceof AccessDeniedException) {
log.warn("Access denied: ", ex);
} else if (ex instanceof OnlyMessageLogExceptionWrapper) {
((OnlyMessageLogExceptionWrapper) ex).log(log);
return new ResponseEntity<>(status);
} else {
log.error("Unexpected generic error catched at the API endpoint: ", ex);
}
@ -83,6 +86,15 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler {
HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(OnlyMessageLogExceptionWrapper.class)
public ResponseEntity<Object> onlyMessageLogExceptionWrapper(
final OnlyMessageLogExceptionWrapper ex,
final WebRequest request) {
ex.log(log);
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(OAuth2Exception.class)
public ResponseEntity<Object> handleOAuth2Exception(
final OAuth2Exception ex,

View file

@ -13,7 +13,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
@ -39,13 +38,13 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigKey;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute;
import ch.ethz.seb.sebserver.gbl.model.user.UserLogActivityType;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Utils;
@ -305,11 +304,12 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
@RequestMapping(
path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT
+ API.TEMPLATE_ATTRIBUTE_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT,
method = RequestMethod.POST,
+ API.MODEL_ID_VAR_PATH_SEGMENT
+ API.TEMPLATE_ATTRIBUTE_RESET_VALUES,
method = RequestMethod.PATCH,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Set<EntityKey> resetTemplateAttribute(
public TemplateAttribute resetTemplateAttributeValues(
@PathVariable(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long parentModelId,
@PathVariable(name = API.PARAM_MODEL_ID, required = true) final Long modelId,
@RequestParam(
@ -323,6 +323,62 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
institutionId,
parentModelId,
modelId)
.flatMap(entity -> this.userActivityLogDAO.log(UserLogActivityType.MODIFY, entity))
.getOrThrow();
}
@RequestMapping(
path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT
+ API.TEMPLATE_ATTRIBUTE_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT
+ API.TEMPLATE_ATTRIBUTE_ATTACH_DEFAUL_ORIENTATION,
method = RequestMethod.PATCH,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public TemplateAttribute attachDefaultTemplateAttributeOrientation(
@PathVariable(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long parentModelId,
@PathVariable(name = API.PARAM_MODEL_ID, required = true) final Long modelId,
@RequestParam(
name = API.PARAM_INSTITUTION_ID,
required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
@RequestParam(name = API.PARAM_VIEW_ID) final Long viewId) {
checkModifyPrivilege(institutionId);
return this.sebExamConfigTemplateService
.attachDefaultOrientation(
institutionId,
parentModelId,
modelId,
viewId)
.flatMap(entity -> this.userActivityLogDAO.log(UserLogActivityType.MODIFY, entity))
.getOrThrow();
}
@RequestMapping(
path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT
+ API.TEMPLATE_ATTRIBUTE_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT
+ API.TEMPLATE_ATTRIBUTE_REMOVE_ORIENTATION,
method = RequestMethod.PATCH,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public TemplateAttribute removeTemplateAttributeOrientation(
@PathVariable(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long parentModelId,
@PathVariable(name = API.PARAM_MODEL_ID, required = true) final Long modelId,
@RequestParam(
name = API.PARAM_INSTITUTION_ID,
required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId) {
checkModifyPrivilege(institutionId);
return this.sebExamConfigTemplateService
.removeOrientation(
institutionId,
parentModelId,
modelId)
.flatMap(entity -> this.userActivityLogDAO.log(UserLogActivityType.MODIFY, entity))
.getOrThrow();
}

View file

@ -326,7 +326,8 @@ public class ExamAPI_V1_Controller {
@RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
@RequestBody(required = true) final ClientEvent event) {
this.sebClientConnectionService.notifyClientEvent(connectionToken, event);
this.sebClientConnectionService
.notifyClientEvent(connectionToken, event);
}
private Long getInstitutionId(final Principal principal) {

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer.api;
import org.slf4j.Logger;
import org.springframework.boot.logging.LogLevel;
public class OnlyMessageLogExceptionWrapper extends RuntimeException {
private static final long serialVersionUID = 4177915563660228494L;
public final LogLevel logLevel;
public OnlyMessageLogExceptionWrapper(final Exception cause) {
super(cause);
this.logLevel = LogLevel.WARN;
}
public OnlyMessageLogExceptionWrapper(final Exception cause, final LogLevel logLevel) {
super(cause);
this.logLevel = logLevel;
}
public final void log(final Logger logger) {
final String message = this.getCause().getMessage();
switch (this.logLevel) {
case ERROR:
case FATAL:
logger.error(message);
break;
case INFO:
logger.info(message);
break;
case TRACE:
logger.trace(message);
break;
case WARN:
logger.warn(message);
break;
default:
logger.debug(message);
break;
}
}
}

View file

@ -1,62 +0,0 @@
# overall server configuration
server.address=0.0.0.0
server.port=8080
server.servlet.context-path=/
server.servlet.session.cookie.http-only=true
server.servlet.session.tracking-modes=cookie
# database server
datastore.mariadb.server.address=seb-server-mariadb
datastore.mariadb.server.port=3306
# data source configuration
spring.datasource.username=root
spring.datasource.initialize=true
spring.datasource.initialization-mode=always
spring.datasource.url=jdbc:mariadb://${datastore.mariadb.server.address}:${datastore.mariadb.server.port}/SEBServer?useSSL=false&createDatabaseIfNotExist=true
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.platform=demo
spring.datasource.hikari.initializationFailTimeout=30000
spring.datasource.hikari.connectionTimeout=30000
spring.datasource.hikari.idleTimeout=600000
spring.datasource.hikari.maxLifetime=1800000
# webservice configuration
sebserver.test.property=DEMO SOURCE CONFIG
sebserver.webservice.distributed=false
sebserver.webservice.http.scheme=http
sebserver.webservice.http.server.name=ralph.ethz.ch
sebserver.webservice.http.redirect.gui=${sebserver.gui.entrypoint}
sebserver.webservice.api.admin.clientId=guiClient
sebserver.webservice.api.admin.endpoint=/admin-api/v1
sebserver.webservice.api.admin.accessTokenValiditySeconds=3600
sebserver.webservice.api.admin.refreshTokenValiditySeconds=-1
sebserver.webservice.api.exam.endpoint=/exam-api
sebserver.webservice.api.exam.endpoint.discovery=${sebserver.webservice.api.exam.endpoint}/discovery
sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1
sebserver.webservice.api.exam.accessTokenValiditySeconds=86400
sebserver.webservice.api.pagination.maxPageSize=500
# comma separated list of known possible OpenEdX API access token request endpoints
sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token
# write logs to
logging.file=log/sebserver.log
# actuator configuration
management.endpoints.web.base-path=/actuator
management.endpoints.web.exposure.include=logfile,loggers
# GUI server configuration
sebserver.gui.entrypoint=/gui
sebserver.gui.webservice.protocol=http
sebserver.gui.webservice.address=${server.address}
sebserver.gui.webservice.port=8080
sebserver.gui.webservice.apipath=/admin-api/v1
sebserver.gui.theme=css/sebserver.css
sebserver.gui.list.page.size=20
sebserver.gui.date.displayformat=MM/dd/yyyy HH:mm
sebserver.gui.date.displayformat.timezone=|ZZ
sebserver.gui.multilingual=false
sebserver.gui.languages=en
sebserver.gui.seb.client.config.download.filename=SEBClientSettings.seb
sebserver.gui.seb.exam.config.download.filename=SEBExamSettings.seb

View file

@ -22,7 +22,7 @@ spring.quartz.properties.org.quartz.threadPool.threadCount=2
sebserver.webservice.distributed=false
sebserver.webservice.http.scheme=http
sebserver.webservice.http.server.name=${server.address}
sebserver.webservice.http.port=${server.port}
sebserver.webservice.http.redirect.gui=/gui
sebserver.webservice.api.admin.endpoint=/admin-api/v1
sebserver.webservice.api.admin.accessTokenValiditySeconds=3600