new import implementation with creating new configuration
This commit is contained in:
parent
da178edf50
commit
9bc8dfaf8b
13 changed files with 338 additions and 72 deletions
|
@ -9,32 +9,41 @@
|
|||
package ch.ethz.seb.sebserver.gui.content;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gui.form.Form;
|
||||
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
|
||||
import ch.ethz.seb.sebserver.gui.form.FormHandle;
|
||||
import ch.ethz.seb.sebserver.gui.service.ResourceService;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialogComposer;
|
||||
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.impl.ModalInputDialog;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ImportExamConfig;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ImportExamConfigOnExistingConfig;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ImportNewExamConfig;
|
||||
import ch.ethz.seb.sebserver.gui.widget.FileUploadSelection;
|
||||
|
||||
public final class SebExamConfigImport {
|
||||
|
||||
static Function<PageAction, PageAction> importConfigFunction(final PageService pageService) {
|
||||
static Function<PageAction, PageAction> importFunction(
|
||||
final PageService pageService,
|
||||
final boolean newConfig) {
|
||||
|
||||
return action -> {
|
||||
|
||||
final ModalInputDialog<FormHandle<ConfigurationNode>> dialog =
|
||||
|
@ -45,13 +54,15 @@ public final class SebExamConfigImport {
|
|||
|
||||
final ImportFormContext importFormContext = new ImportFormContext(
|
||||
pageService,
|
||||
action.pageContext());
|
||||
action.pageContext(),
|
||||
newConfig);
|
||||
|
||||
dialog.open(
|
||||
SebExamConfigPropForm.FORM_IMPORT_TEXT_KEY,
|
||||
(Consumer<FormHandle<ConfigurationNode>>) formHandle -> doImport(
|
||||
(Predicate<FormHandle<ConfigurationNode>>) formHandle -> doImport(
|
||||
pageService,
|
||||
formHandle),
|
||||
formHandle,
|
||||
newConfig),
|
||||
importFormContext::cancelUpload,
|
||||
importFormContext);
|
||||
|
||||
|
@ -59,39 +70,90 @@ public final class SebExamConfigImport {
|
|||
};
|
||||
}
|
||||
|
||||
private static final void doImport(
|
||||
private static final boolean doImport(
|
||||
final PageService pageService,
|
||||
final FormHandle<ConfigurationNode> formHandle) {
|
||||
final FormHandle<ConfigurationNode> formHandle,
|
||||
final boolean newConfig) {
|
||||
|
||||
final Form form = formHandle.getForm();
|
||||
final EntityKey entityKey = formHandle.getContext().getEntityKey();
|
||||
final Control fieldControl = form.getFieldControl(API.IMPORT_FILE_ATTR_NAME);
|
||||
final PageContext context = formHandle.getContext();
|
||||
if (fieldControl != null && fieldControl instanceof FileUploadSelection) {
|
||||
final FileUploadSelection fileUpload = (FileUploadSelection) fieldControl;
|
||||
final InputStream inputStream = fileUpload.getInputStream();
|
||||
if (inputStream != null) {
|
||||
final Configuration configuration = pageService.getRestService()
|
||||
.getBuilder(ImportExamConfig.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
.withHeader(
|
||||
API.IMPORT_PASSWORD_ATTR_NAME,
|
||||
form.getFieldValue(API.IMPORT_PASSWORD_ATTR_NAME))
|
||||
.withBody(inputStream)
|
||||
.call()
|
||||
.get(e -> {
|
||||
fileUpload.close();
|
||||
return context.notifyError(e);
|
||||
});
|
||||
try {
|
||||
final Form form = formHandle.getForm();
|
||||
final EntityKey entityKey = formHandle.getContext().getEntityKey();
|
||||
final Control fieldControl = form.getFieldControl(API.IMPORT_FILE_ATTR_NAME);
|
||||
final PageContext context = formHandle.getContext();
|
||||
|
||||
if (configuration != null) {
|
||||
context.publishInfo(SebExamConfigPropForm.FORM_IMPORT_CONFIRM_TEXT_KEY);
|
||||
}
|
||||
} else {
|
||||
formHandle.getContext().publishPageMessage(
|
||||
new LocTextKey("sebserver.error.unexpected"),
|
||||
new LocTextKey("Please selecte a valid SEB Exam Configuration File"));
|
||||
// Ad-hoc field validation
|
||||
formHandle.process(name -> true, field -> field.resetError());
|
||||
final String fieldValue = form.getFieldValue(Domain.CONFIGURATION_NODE.ATTR_NAME);
|
||||
if (StringUtils.isBlank(fieldValue)) {
|
||||
form.setFieldError(
|
||||
Domain.CONFIGURATION_NODE.ATTR_NAME,
|
||||
pageService
|
||||
.getI18nSupport()
|
||||
.getText(new LocTextKey("sebserver.form.validation.fieldError.notNull")));
|
||||
return false;
|
||||
} else if (fieldValue.length() < 3 || fieldValue.length() > 255) {
|
||||
form.setFieldError(
|
||||
Domain.CONFIGURATION_NODE.ATTR_NAME,
|
||||
pageService
|
||||
.getI18nSupport()
|
||||
.getText(new LocTextKey("sebserver.form.validation.fieldError.size",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
3,
|
||||
255)));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fieldControl != null && fieldControl instanceof FileUploadSelection) {
|
||||
final FileUploadSelection fileUpload = (FileUploadSelection) fieldControl;
|
||||
final InputStream inputStream = fileUpload.getInputStream();
|
||||
if (inputStream != null) {
|
||||
final RestCall<Configuration>.RestCallBuilder restCall = (newConfig)
|
||||
? pageService.getRestService()
|
||||
.getBuilder(ImportNewExamConfig.class)
|
||||
: pageService.getRestService()
|
||||
.getBuilder(ImportExamConfigOnExistingConfig.class);
|
||||
|
||||
restCall
|
||||
.withHeader(
|
||||
API.IMPORT_PASSWORD_ATTR_NAME,
|
||||
form.getFieldValue(API.IMPORT_PASSWORD_ATTR_NAME))
|
||||
.withBody(inputStream);
|
||||
|
||||
if (newConfig) {
|
||||
restCall
|
||||
.withHeader(
|
||||
Domain.CONFIGURATION_NODE.ATTR_NAME,
|
||||
form.getFieldValue(Domain.CONFIGURATION_NODE.ATTR_NAME))
|
||||
.withHeader(
|
||||
Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION,
|
||||
form.getFieldValue(Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION))
|
||||
.withHeader(
|
||||
Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID,
|
||||
form.getFieldValue(Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID));
|
||||
} else {
|
||||
restCall.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId);
|
||||
}
|
||||
|
||||
final Result<Configuration> configuration = restCall
|
||||
.call();
|
||||
|
||||
if (!configuration.hasError()) {
|
||||
context.publishInfo(SebExamConfigPropForm.FORM_IMPORT_CONFIRM_TEXT_KEY);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
formHandle.getContext().publishPageMessage(
|
||||
new LocTextKey("sebserver.error.unexpected"),
|
||||
new LocTextKey("Please selecte a valid SEB Exam Configuration File"));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (final Exception e) {
|
||||
formHandle.getContext().notifyError(e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,17 +161,25 @@ public final class SebExamConfigImport {
|
|||
|
||||
private final PageService pageService;
|
||||
private final PageContext pageContext;
|
||||
private final boolean newConfig;
|
||||
|
||||
private Form form = null;
|
||||
|
||||
protected ImportFormContext(final PageService pageService, final PageContext pageContext) {
|
||||
protected ImportFormContext(
|
||||
final PageService pageService,
|
||||
final PageContext pageContext,
|
||||
final boolean newConfig) {
|
||||
|
||||
this.pageService = pageService;
|
||||
this.pageContext = pageContext;
|
||||
this.newConfig = newConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<FormHandle<ConfigurationNode>> compose(final Composite parent) {
|
||||
|
||||
final ResourceService resourceService = this.pageService.getResourceService();
|
||||
|
||||
final FormHandle<ConfigurationNode> formHandle = this.pageService.formBuilder(
|
||||
this.pageContext.copyOf(parent), 4)
|
||||
.readonly(false)
|
||||
|
@ -118,6 +188,26 @@ public final class SebExamConfigImport {
|
|||
SebExamConfigPropForm.FORM_IMPORT_SELECT_TEXT_KEY,
|
||||
null,
|
||||
API.SEB_FILE_EXTENSION))
|
||||
|
||||
.addFieldIf(
|
||||
() -> this.newConfig,
|
||||
() -> FormBuilder.text(
|
||||
Domain.CONFIGURATION_NODE.ATTR_NAME,
|
||||
SebExamConfigPropForm.FORM_NAME_TEXT_KEY))
|
||||
.addFieldIf(
|
||||
() -> this.newConfig,
|
||||
() -> FormBuilder.text(
|
||||
Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION,
|
||||
SebExamConfigPropForm.FORM_DESCRIPTION_TEXT_KEY)
|
||||
.asArea())
|
||||
.addFieldIf(
|
||||
() -> this.newConfig,
|
||||
() -> FormBuilder.singleSelection(
|
||||
Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID,
|
||||
SebExamConfigPropForm.FORM_TEMPLATE_TEXT_KEY,
|
||||
null,
|
||||
resourceService::getExamConfigTemplateResources))
|
||||
|
||||
.addField(FormBuilder.text(
|
||||
API.IMPORT_PASSWORD_ATTR_NAME,
|
||||
SebExamConfigPropForm.FORM_IMPORT_PASSWORD_TEXT_KEY,
|
||||
|
|
|
@ -207,6 +207,11 @@ public class SebExamConfigList implements TemplateComposer {
|
|||
PageAction::applySingleSelection, EMPTY_SELECTION_TEXT_KEY)
|
||||
.publishIf(() -> examConfigGrant.im() && configTable.hasAnyContent())
|
||||
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_TO_NEW_CONFIG)
|
||||
.withExec(SebExamConfigImport.importFunction(this.pageService, true))
|
||||
.noEventPropagation()
|
||||
.publishIf(() -> examConfigGrant.im())
|
||||
|
||||
// Exam Configuration template actions...
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_NEW)
|
||||
.publishIf(examConfigGrant::iw)
|
||||
|
|
|
@ -246,9 +246,9 @@ public class SebExamConfigPropForm implements TemplateComposer {
|
|||
.noEventPropagation()
|
||||
.publishIf(() -> modifyGrant && isReadonly)
|
||||
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_CONFIG)
|
||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_TO_EXISTING_CONFIG)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(SebExamConfigImport.importConfigFunction(this.pageService))
|
||||
.withExec(SebExamConfigImport.importFunction(this.pageService, false))
|
||||
.noEventPropagation()
|
||||
.publishIf(() -> modifyGrant && isReadonly && !isAttachedToExam)
|
||||
|
||||
|
|
|
@ -368,6 +368,10 @@ public enum ActionDefinition {
|
|||
ImageIcon.SHOW,
|
||||
PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW,
|
||||
ActionCategory.FORM),
|
||||
SEB_EXAM_CONFIG_IMPORT_TO_NEW_CONFIG(
|
||||
new LocTextKey("sebserver.examconfig.action.import-config"),
|
||||
ImageIcon.IMPORT,
|
||||
ActionCategory.VARIA),
|
||||
|
||||
SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST(
|
||||
new LocTextKey("sebserver.examconfig.action.list.modify.properties"),
|
||||
|
@ -407,10 +411,11 @@ public enum ActionDefinition {
|
|||
new LocTextKey("sebserver.examconfig.action.get-config-key"),
|
||||
ImageIcon.SECURE,
|
||||
ActionCategory.FORM),
|
||||
SEB_EXAM_CONFIG_IMPORT_CONFIG(
|
||||
SEB_EXAM_CONFIG_IMPORT_TO_EXISTING_CONFIG(
|
||||
new LocTextKey("sebserver.examconfig.action.import-config"),
|
||||
ImageIcon.IMPORT,
|
||||
ActionCategory.FORM),
|
||||
|
||||
SEB_EXAM_CONFIG_COPY_CONFIG(
|
||||
new LocTextKey("sebserver.examconfig.action.copy"),
|
||||
ImageIcon.COPY,
|
||||
|
|
|
@ -227,6 +227,15 @@ public final class Form implements FormBinding {
|
|||
.isPresent();
|
||||
}
|
||||
|
||||
public void setFieldError(final String fieldName, final String errorMessage) {
|
||||
final List<FormFieldAccessor> list = this.formFields.get(fieldName);
|
||||
if (list != null) {
|
||||
list
|
||||
.stream()
|
||||
.forEach(ffa -> ffa.setError(errorMessage));
|
||||
}
|
||||
}
|
||||
|
||||
public void process(
|
||||
final Predicate<String> nameFilter,
|
||||
final Consumer<FormFieldAccessor> processor) {
|
||||
|
|
|
@ -24,9 +24,9 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
|||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class ImportExamConfig extends RestCall<Configuration> {
|
||||
public class ImportExamConfigOnExistingConfig extends RestCall<Configuration> {
|
||||
|
||||
public ImportExamConfig() {
|
||||
public ImportExamConfigOnExistingConfig() {
|
||||
super(new TypeKey<>(
|
||||
CallType.UNDEFINED,
|
||||
EntityType.CONFIGURATION,
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.Configuration;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class ImportNewExamConfig extends RestCall<Configuration> {
|
||||
|
||||
public ImportNewExamConfig() {
|
||||
super(new TypeKey<>(
|
||||
CallType.UNDEFINED,
|
||||
EntityType.CONFIGURATION,
|
||||
new TypeReference<Configuration>() {
|
||||
}),
|
||||
HttpMethod.POST,
|
||||
MediaType.APPLICATION_OCTET_STREAM,
|
||||
API.CONFIGURATION_NODE_ENDPOINT + API.CONFIGURATION_IMPORT_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
}
|
|
@ -13,6 +13,7 @@ import java.util.Set;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
||||
public interface ConfigurationDAO extends EntityDAO<Configuration, Configuration> {
|
||||
|
@ -48,6 +49,31 @@ public interface ConfigurationDAO extends EntityDAO<Configuration, Configuration
|
|||
* @return the current and reseted follow-up version */
|
||||
Result<Configuration> undo(Long configurationNodeId);
|
||||
|
||||
/** Restores the attribute values to the default values that have been set for the specified configuration
|
||||
* on initialization. This are the base default values if the configuration has no template or the default
|
||||
* values from the template if there is one assigned to the configuration.
|
||||
*
|
||||
* In fact. this just gets the initial configuration values and reset the current values with that one
|
||||
*
|
||||
* @param configurationNodeId the ConfigurationNode identifier
|
||||
* @return the Configuration instance for which the attribute values have been reset */
|
||||
Result<Configuration> restoreToDefaultValues(final Long configurationNodeId);
|
||||
|
||||
/** Restores the attribute values to the default values that have been set for the specified configuration
|
||||
* on initialization. This are the base default values if the configuration has no template or the default
|
||||
* values from the template if there is one assigned to the configuration.
|
||||
*
|
||||
* In fact. this just gets the initial configuration values and reset the current values with that one
|
||||
*
|
||||
* @param configuration the Configuration that defines the ConfigurationNode identifier
|
||||
* @return the Configuration instance for which the attribute values have been reset */
|
||||
default Result<Configuration> restoreToDefaultValues(final Configuration configuration) {
|
||||
if (configuration == null) {
|
||||
return Result.ofError(new NullPointerException("configuration"));
|
||||
}
|
||||
return restoreToDefaultValues(configuration.configurationNodeId);
|
||||
}
|
||||
|
||||
/** Restores the current follow-up Configuration to the values of a given Configuration
|
||||
* in the history of the specified ConfigurationNode.
|
||||
*
|
||||
|
@ -62,6 +88,17 @@ public interface ConfigurationDAO extends EntityDAO<Configuration, Configuration
|
|||
* @return the current follow-up configuration */
|
||||
Result<Configuration> getFollowupConfiguration(Long configNodeId);
|
||||
|
||||
/** Use this to get the follow-up configuration for a specified configuration node.
|
||||
*
|
||||
* @param configNode ConfigurationNode to get the current follow-up configuration from
|
||||
* @return the current follow-up configuration */
|
||||
default Result<Configuration> getFollowupConfiguration(final ConfigurationNode configurationNode) {
|
||||
if (configurationNode == null) {
|
||||
return Result.ofError(new NullPointerException("configurationNode"));
|
||||
}
|
||||
return getFollowupConfiguration(configurationNode.id);
|
||||
}
|
||||
|
||||
/** Use this to get the last version of a configuration that is not the follow-up.
|
||||
*
|
||||
* @param configNodeId ConfigurationNode identifier to get the last version of configuration from
|
||||
|
|
|
@ -266,6 +266,25 @@ class ConfigurationDAOBatchService {
|
|||
.flatMap(rec -> restoreToVersion(configurationNodeId, rec.getId()));
|
||||
}
|
||||
|
||||
Result<Configuration> restoreToDefaultValues(final Long configurationNodeId) {
|
||||
return Result.tryCatch(() -> {
|
||||
// get initial version that contains the default values either from base or from template
|
||||
return this.batchConfigurationRecordMapper.selectIdsByExample()
|
||||
.where(
|
||||
ConfigurationRecordDynamicSqlSupport.configurationNodeId,
|
||||
isEqualTo(configurationNodeId))
|
||||
.and(
|
||||
ConfigurationRecordDynamicSqlSupport.version,
|
||||
isEqualTo(INITIAL_VERSION_NAME))
|
||||
.build()
|
||||
.execute()
|
||||
.stream()
|
||||
.collect(Utils.toSingleton());
|
||||
|
||||
})
|
||||
.flatMap(configId -> restoreToVersion(configurationNodeId, configId));
|
||||
}
|
||||
|
||||
Result<Configuration> restoreToVersion(final Long configurationNodeId, final Long configId) {
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
|
|
|
@ -193,6 +193,14 @@ public class ConfigurationDAOImpl implements ConfigurationDAO {
|
|||
.onError(TransactionHandler::rollback);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<Configuration> restoreToDefaultValues(final Long configurationNodeId) {
|
||||
return this.configurationDAOBatchService
|
||||
.restoreToDefaultValues(configurationNodeId)
|
||||
.onError(TransactionHandler::rollback);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<Configuration> restoreToVersion(final Long configurationNodeId, final Long configId) {
|
||||
|
|
|
@ -118,10 +118,10 @@ public interface SebExamConfigService {
|
|||
*
|
||||
* Then parses the XML and adds each attribute to the new Configuration.
|
||||
*
|
||||
* @param configNodeId The identifier of the configuration node on which the import should take place
|
||||
* @param config The Configuration to import the attribute values to
|
||||
* @param input The InputStream to get the SEB config file as byte-stream
|
||||
* @param password A password is only needed if the file is in an encrypted format
|
||||
* @return The newly created Configuration instance */
|
||||
Result<Configuration> importFromSEBFile(Long configNodeId, InputStream input, CharSequence password);
|
||||
Result<Configuration> importFromSEBFile(Configuration config, InputStream input, CharSequence password);
|
||||
|
||||
}
|
||||
|
|
|
@ -330,16 +330,12 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
|
|||
|
||||
@Override
|
||||
public Result<Configuration> importFromSEBFile(
|
||||
final Long configNodeId,
|
||||
final Configuration config,
|
||||
final InputStream input,
|
||||
final CharSequence password) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
final Configuration newConfig = this.configurationDAO
|
||||
.saveToHistory(configNodeId)
|
||||
.getOrThrow();
|
||||
|
||||
Future<Exception> streamDecrypted = null;
|
||||
InputStream cryptIn = null;
|
||||
PipedInputStream plainIn = null;
|
||||
|
@ -363,16 +359,16 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
|
|||
// parse XML and import
|
||||
this.examConfigIO.importPlainXML(
|
||||
unzippedIn,
|
||||
newConfig.institutionId,
|
||||
newConfig.id);
|
||||
config.institutionId,
|
||||
config.id);
|
||||
|
||||
return newConfig;
|
||||
return config;
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Unexpected error while trying to import SEB Exam Configuration: ", e);
|
||||
log.debug("Make an undo on the ConfigurationNode to rollback the changes");
|
||||
this.configurationDAO
|
||||
.undo(configNodeId)
|
||||
.undo(config.configurationNodeId)
|
||||
.getOrThrow();
|
||||
|
||||
if (streamDecrypted != null) {
|
||||
|
|
|
@ -20,6 +20,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
import javax.validation.Valid;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.mybatis.dynamic.sql.SqlTable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -39,12 +40,14 @@ import ch.ethz.seb.sebserver.gbl.api.API;
|
|||
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;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigCopyInfo;
|
||||
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.ConfigurationStatus;
|
||||
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;
|
||||
|
@ -222,11 +225,50 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
|
|||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.MODEL_ID_VAR_PATH_SEGMENT + API.CONFIGURATION_IMPORT_PATH_SEGMENT,
|
||||
path = API.CONFIGURATION_IMPORT_PATH_SEGMENT,
|
||||
method = RequestMethod.POST,
|
||||
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public Object importExamConfig(
|
||||
@RequestHeader(name = Domain.CONFIGURATION_NODE.ATTR_NAME, required = false) final String name,
|
||||
@RequestHeader(name = Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION,
|
||||
required = false) final String description,
|
||||
@RequestHeader(name = Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID, required = false) final String templateId,
|
||||
@RequestHeader(name = API.IMPORT_PASSWORD_ATTR_NAME, required = false) final String password,
|
||||
@RequestParam(
|
||||
name = API.PARAM_INSTITUTION_ID,
|
||||
required = true,
|
||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||
final HttpServletRequest request) throws IOException {
|
||||
|
||||
this.checkModifyPrivilege(institutionId);
|
||||
|
||||
final SEBServerUser currentUser = this.authorization.getUserService().getCurrentUser();
|
||||
|
||||
final ConfigurationNode configurationNode = new ConfigurationNode(
|
||||
null,
|
||||
institutionId,
|
||||
StringUtils.isNotBlank(templateId) ? Long.parseLong(templateId) : null,
|
||||
name,
|
||||
description,
|
||||
ConfigurationType.EXAM_CONFIG,
|
||||
currentUser.uuid(),
|
||||
ConfigurationStatus.CONSTRUCTION);
|
||||
|
||||
final Configuration followup = this.beanValidationService.validateBean(configurationNode)
|
||||
.flatMap(this.entityDAO::createNew)
|
||||
.flatMap(this.configurationDAO::getFollowupConfiguration)
|
||||
.getOrThrow();
|
||||
|
||||
return doImport(password, request, followup);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.MODEL_ID_VAR_PATH_SEGMENT + API.CONFIGURATION_IMPORT_PATH_SEGMENT,
|
||||
method = RequestMethod.POST,
|
||||
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public Object importExamConfigOnExistingConfig(
|
||||
@PathVariable final Long modelId,
|
||||
@RequestHeader(name = API.IMPORT_PASSWORD_ATTR_NAME, required = false) final String password,
|
||||
@RequestParam(
|
||||
|
@ -235,27 +277,15 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
|
|||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||
final HttpServletRequest request) throws IOException {
|
||||
|
||||
final InputStream inputStream = new BufferedInputStream(request.getInputStream());
|
||||
try {
|
||||
this.entityDAO.byPK(modelId)
|
||||
.flatMap(this.authorization::checkModify);
|
||||
|
||||
return this.sebExamConfigService.importFromSEBFile(
|
||||
modelId,
|
||||
inputStream,
|
||||
password)
|
||||
.getOrThrow();
|
||||
final Configuration newConfig = this.configurationDAO
|
||||
.saveToHistory(modelId)
|
||||
.flatMap(this.configurationDAO::restoreToDefaultValues)
|
||||
.getOrThrow();
|
||||
|
||||
} catch (final Exception e) {
|
||||
// NOTE: It seems that this has to be manually closed on error case
|
||||
// We expected that this is closed by the API but if this manual close is been left
|
||||
// some left-overs will affect strange behavior.
|
||||
// TODO: find a better solution for this
|
||||
IOUtils.closeQuietly(inputStream);
|
||||
//throw e;
|
||||
return new ResponseEntity<>(
|
||||
Arrays.asList(APIMessage.ErrorMessage.UNEXPECTED.of(e.getMessage())),
|
||||
Utils.createJsonContentHeader(),
|
||||
HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
return doImport(password, request, newConfig);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
|
@ -444,4 +474,31 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
|
|||
return node;
|
||||
}
|
||||
|
||||
private Object doImport(
|
||||
final String password,
|
||||
final HttpServletRequest request,
|
||||
final Configuration configuration) throws IOException {
|
||||
final InputStream inputStream = new BufferedInputStream(request.getInputStream());
|
||||
try {
|
||||
|
||||
return this.sebExamConfigService.importFromSEBFile(
|
||||
configuration,
|
||||
inputStream,
|
||||
password)
|
||||
.getOrThrow();
|
||||
|
||||
} catch (final Exception e) {
|
||||
// NOTE: It seems that this has to be manually closed on error case
|
||||
// We expected that this is closed by the API but if this manual close is been left
|
||||
// some left-overs will affect strange behavior.
|
||||
// TODO: find a better solution for this
|
||||
IOUtils.closeQuietly(inputStream);
|
||||
|
||||
return new ResponseEntity<>(
|
||||
Arrays.asList(APIMessage.ErrorMessage.UNEXPECTED.of(e.getMessage())),
|
||||
Utils.createJsonContentHeader(),
|
||||
HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue