SEBSERV-30 preparing for Exam and Quiz gui and javadoc

This commit is contained in:
anhefti 2019-03-14 16:56:34 +01:00
parent 7b2f7228af
commit d404498475
35 changed files with 877 additions and 196 deletions

View file

@ -48,7 +48,7 @@ public final class API {
public static final String USER_ACCOUNT_ENDPOINT = "/useraccount";
public static final String QUIZ_IMPORT_ENDPOINT = "/quiz";
public static final String QUIZ_DISCOVERY_ENDPOINT = "/quiz";
public static final String EXAM_ADMINISTRATION_ENDPOINT = "/exam";

View file

@ -27,6 +27,7 @@ public final class QuizData implements Entity {
public static final String FILTER_ATTR_START_TIME = "start_timestamp";
public static final String QUIZ_ATTR_ID = "quiz_id";
public static final String QUIZ_ATTR_LMS_SETUP_ID = "lms_setup_id";
public static final String QUIZ_ATTR_NAME = "quiz_name";
public static final String QUIZ_ATTR_DESCRIPTION = "quiz_description";
public static final String QUIZ_ATTR_START_TIME = "quiz_start_time";
@ -36,6 +37,9 @@ public final class QuizData implements Entity {
@JsonProperty(QUIZ_ATTR_ID)
public final String id;
@JsonProperty(QUIZ_ATTR_LMS_SETUP_ID)
public final String lmsSetupId;
@JsonProperty(QUIZ_ATTR_NAME)
public final String name;
@ -54,6 +58,7 @@ public final class QuizData implements Entity {
@JsonCreator
public QuizData(
@JsonProperty(QUIZ_ATTR_ID) final String id,
@JsonProperty(QUIZ_ATTR_LMS_SETUP_ID) final String lmsSetupId,
@JsonProperty(QUIZ_ATTR_NAME) final String name,
@JsonProperty(QUIZ_ATTR_DESCRIPTION) final String description,
@JsonProperty(QUIZ_ATTR_START_TIME) final DateTime startTime,
@ -61,6 +66,7 @@ public final class QuizData implements Entity {
@JsonProperty(QUIZ_ATTR_START_URL) final String startURL) {
this.id = id;
this.lmsSetupId = lmsSetupId;
this.name = name;
this.description = description;
this.startTime = startTime;
@ -70,6 +76,7 @@ public final class QuizData implements Entity {
public QuizData(
final String id,
final String lmsSetupId,
final String name,
final String description,
final String startTime,
@ -77,6 +84,7 @@ public final class QuizData implements Entity {
final String startURL) {
this.id = id;
this.lmsSetupId = lmsSetupId;
this.name = name;
this.description = description;
this.startTime = LocalDateTime
@ -106,6 +114,10 @@ public final class QuizData implements Entity {
return this.id;
}
public String getLmsSetupId() {
return this.lmsSetupId;
}
@Override
public String getName() {
return this.name;
@ -154,9 +166,9 @@ public final class QuizData implements Entity {
@Override
public String toString() {
return "QuizData [id=" + this.id + ", name=" + this.name + ", description=" + this.description + ", startTime="
+ this.startTime
+ ", endTime=" + this.endTime + ", startURL=" + this.startURL + "]";
return "QuizData [id=" + this.id + ", lmsSetupId=" + this.lmsSetupId + ", name=" + this.name + ", description="
+ this.description
+ ", startTime=" + this.startTime + ", endTime=" + this.endTime + ", startURL=" + this.startURL + "]";
}
public static Comparator<QuizData> getIdComparator(final boolean descending) {

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.content;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.form.PageFormService;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
@Lazy
@Component
@GuiProfile
public class ExamForm implements TemplateComposer {
private final PageFormService pageFormService;
private final ResourceService resourceService;
protected ExamForm(
final PageFormService pageFormService,
final ResourceService resourceService) {
this.pageFormService = pageFormService;
this.resourceService = resourceService;
}
@Override
public void compose(final PageContext pageContext) {
// TODO Auto-generated method stub
}
}

View file

@ -0,0 +1,46 @@
/*
* 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.content;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class ExamList implements TemplateComposer {
private final WidgetFactory widgetFactory;
private final ResourceService resourceService;
private final int pageSize;
protected ExamList(
final WidgetFactory widgetFactory,
final ResourceService resourceService,
@Value("${sebserver.gui.list.page.size}") final Integer pageSize) {
this.widgetFactory = widgetFactory;
this.resourceService = resourceService;
this.pageSize = (pageSize != null) ? pageSize : 20;
}
@Override
public void compose(final PageContext pageContext) {
// TODO Auto-generated method stub
}
}

View file

@ -114,7 +114,7 @@ public class LmsSetupForm implements TemplateComposer {
final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(lmsSetup);
final boolean writeGrant = userGrantCheck.w();
final boolean modifyGrant = userGrantCheck.m();
final boolean istitutionActive = restService.getBuilder(GetInstitution.class)
final boolean institutionActive = restService.getBuilder(GetInstitution.class)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(lmsSetup.getInstitutionId()))
.call()
.map(inst -> inst.active)
@ -177,27 +177,27 @@ public class LmsSetupForm implements TemplateComposer {
formContext.clearEntityKeys()
.createAction(ActionDefinition.LMS_SETUP_NEW)
.publishIf(() -> writeGrant && readonly && istitutionActive)
.publishIf(() -> writeGrant && readonly && institutionActive)
.createAction(ActionDefinition.LMS_SETUP_MODIFY)
.withEntityKey(entityKey)
.publishIf(() -> modifyGrant && readonly && istitutionActive)
.publishIf(() -> modifyGrant && readonly && institutionActive)
.createAction(ActionDefinition.LMS_SETUP_TEST)
.withEntityKey(entityKey)
.withExec(action -> this.testLmsSetup(action, formHandle))
.publishIf(() -> modifyGrant && isNotNew.getAsBoolean() && istitutionActive)
.publishIf(() -> modifyGrant && isNotNew.getAsBoolean() && institutionActive)
.createAction(ActionDefinition.LMS_SETUP_DEACTIVATE)
.withEntityKey(entityKey)
.withExec(restService::activation)
.withConfirm(PageUtils.confirmDeactivation(lmsSetup, restService))
.publishIf(() -> writeGrant && readonly && istitutionActive && lmsSetup.isActive())
.publishIf(() -> writeGrant && readonly && institutionActive && lmsSetup.isActive())
.createAction(ActionDefinition.LMS_SETUP_ACTIVATE)
.withEntityKey(entityKey)
.withExec(restService::activation)
.publishIf(() -> writeGrant && readonly && istitutionActive && !lmsSetup.isActive())
.publishIf(() -> writeGrant && readonly && institutionActive && !lmsSetup.isActive())
.createAction(ActionDefinition.LMS_SETUP_SAVE)
.withExec(formHandle::processFormSave)
@ -208,7 +208,6 @@ public class LmsSetupForm implements TemplateComposer {
.withExec(Action::onEmptyEntityKeyGoToActivityHome)
.withConfirm("sebserver.overall.action.modify.cancel.confirm")
.publishIf(() -> !readonly);
}
/** LmsSetup test action implementation */

View file

@ -0,0 +1,46 @@
/*
* 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.content;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class QuizDiscoveryList implements TemplateComposer {
private final WidgetFactory widgetFactory;
private final ResourceService resourceService;
private final int pageSize;
protected QuizDiscoveryList(
final WidgetFactory widgetFactory,
final ResourceService resourceService,
@Value("${sebserver.gui.list.page.size}") final Integer pageSize) {
this.widgetFactory = widgetFactory;
this.resourceService = resourceService;
this.pageSize = (pageSize != null) ? pageSize : 20;
}
@Override
public void compose(final PageContext pageContext) {
// TODO Auto-generated method stub
}
}

View file

@ -106,7 +106,7 @@ public class UserAccountForm implements TemplateComposer {
final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(userAccount);
final boolean writeGrant = userGrantCheck.w();
final boolean modifyGrant = userGrantCheck.m();
final boolean istitutionActive = restService.getBuilder(GetInstitution.class)
final boolean institutionActive = restService.getBuilder(GetInstitution.class)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(userAccount.getInstitutionId()))
.call()
.map(inst -> inst.active)
@ -190,26 +190,26 @@ public class UserAccountForm implements TemplateComposer {
formContext.clearEntityKeys()
.createAction(ActionDefinition.USER_ACCOUNT_NEW)
.publishIf(() -> writeGrant && readonly && istitutionActive)
.publishIf(() -> writeGrant && readonly && institutionActive)
.createAction(ActionDefinition.USER_ACCOUNT_MODIFY)
.withEntityKey(entityKey)
.publishIf(() -> modifyGrant && readonly && istitutionActive)
.publishIf(() -> modifyGrant && readonly && institutionActive)
.createAction(ActionDefinition.USER_ACCOUNT_CHANGE_PASSOWRD)
.withEntityKey(entityKey)
.publishIf(() -> modifyGrant && readonly && istitutionActive && userAccount.isActive())
.publishIf(() -> modifyGrant && readonly && institutionActive && userAccount.isActive())
.createAction(ActionDefinition.USER_ACCOUNT_DEACTIVATE)
.withEntityKey(entityKey)
.withExec(restService::activation)
.withConfirm(PageUtils.confirmDeactivation(userAccount, restService))
.publishIf(() -> writeGrant && readonly && istitutionActive && userAccount.isActive())
.publishIf(() -> writeGrant && readonly && institutionActive && userAccount.isActive())
.createAction(ActionDefinition.USER_ACCOUNT_ACTIVATE)
.withEntityKey(entityKey)
.withExec(restService::activation)
.publishIf(() -> writeGrant && readonly && istitutionActive && !userAccount.isActive())
.publishIf(() -> writeGrant && readonly && institutionActive && !userAccount.isActive())
.createAction(ActionDefinition.USER_ACCOUNT_SAVE)
.withExec(action -> {

View file

@ -216,6 +216,7 @@ public final class Form implements FormBinding {
}
}
// following are FormFieldAccessor implementations for all field types
//@formatter:off
private FormFieldAccessor createAccessor(final Label label, final Label field) {
return new FormFieldAccessor(label, field) {
@ -229,24 +230,17 @@ public final class Form implements FormBinding {
@Override public void setValue(final String value) { text.setText(value); }
};
}
private FormFieldAccessor createAccessor(
final Label label,
final SingleSelection singleSelection) {
private FormFieldAccessor createAccessor(final Label label, final SingleSelection singleSelection) {
return new FormFieldAccessor(label, singleSelection) {
@Override public String getValue() { return singleSelection.getSelectionValue(); }
@Override public void setValue(final String value) { singleSelection.select(value); }
};
}
private FormFieldAccessor createAccessor(
final Label label,
final MultiSelection multiSelection) {
private FormFieldAccessor createAccessor(final Label label,final MultiSelection multiSelection) {
return new FormFieldAccessor(label, multiSelection) {
@Override public String getValue() { return multiSelection.getSelectionValue(); }
@Override public void setValue(final String value) { multiSelection.select(value); }
@Override
public void putJsonValue(final String key, final ObjectNode objectRoot) {
@Override public void putJsonValue(final String key, final ObjectNode objectRoot) {
final String value = getValue();
if (StringUtils.isNoneBlank(value)) {
final ArrayNode arrayNode = objectRoot.putArray(key);

View file

@ -31,6 +31,7 @@ import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionNames;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetupNames;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
@Lazy
@ -115,6 +116,35 @@ public class ResourceService {
};
}
public List<Tuple<String>> lmsSetupResource(final Long institutionId) {
return this.restService.getBuilder(GetLmsSetupNames.class)
.withQueryParam(Domain.LMS_SETUP.ATTR_INSTITUTION_ID, String.valueOf(institutionId))
.withQueryParam(Domain.LMS_SETUP.ATTR_ACTIVE, "true")
.call()
.getOr(Collections.emptyList())
.stream()
.map(entityName -> new Tuple<>(entityName.modelId, entityName.name))
.collect(Collectors.toList());
}
public Function<String, String> getLmsSetupNameFunction(final Long institutionId) {
final Map<String, String> idNameMap = this.restService.getBuilder(GetLmsSetupNames.class)
.withQueryParam(Domain.LMS_SETUP.ATTR_INSTITUTION_ID, String.valueOf(institutionId))
.withQueryParam(Domain.INSTITUTION.ATTR_ACTIVE, "true")
.call()
.getOr(Collections.emptyList())
.stream()
.collect(Collectors.toMap(e -> e.modelId, e -> e.name));
return id -> {
if (!idNameMap.containsKey(id)) {
return Constants.EMPTY_NOTE;
}
return idNameMap.get(id);
};
}
/** Get a list of language key/name tuples for all supported languages in the
* language of the current users locale.
*

View file

@ -16,6 +16,10 @@ import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
/** Puts RAP's server-push functionality in a well defined service by using a context
* as state holder and the possibility to split the server-push process into two
* separated processes, a business-process to get and update business data and the
* an update-process to update the UI after according to updated data */
@Lazy
@Service
public class ServerPushService {

View file

@ -8,6 +8,7 @@
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@ -25,8 +26,10 @@ import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientResponseException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
@ -100,20 +103,7 @@ public abstract class RestCall<T> {
RestCall.this.typeKey.typeRef));
} else {
final RestCallError restCallError =
new RestCallError("Response Entity: " + responseEntity.toString());
restCallError.errors.addAll(RestCall.this.jsonMapper.readValue(
responseEntity.getBody(),
new TypeReference<List<APIMessage>>() {
}));
log.debug(
"Webservice answered with well defined error- or validation-failure-response: ",
restCallError);
return Result.ofError(restCallError);
return handleRestCallError(responseEntity);
}
} catch (final Throwable t) {
@ -149,6 +139,24 @@ public abstract class RestCall<T> {
return new RestCallBuilder();
}
private Result<T> handleRestCallError(final ResponseEntity<String> responseEntity)
throws IOException, JsonParseException, JsonMappingException {
final RestCallError restCallError =
new RestCallError("Response Entity: " + responseEntity.toString());
restCallError.errors.addAll(RestCall.this.jsonMapper.readValue(
responseEntity.getBody(),
new TypeReference<List<APIMessage>>() {
}));
log.debug(
"Webservice answered with well defined error- or validation-failure-response: ",
restCallError);
return Result.ofError(restCallError);
}
public class RestCallBuilder {
private final HttpHeaders httpHeaders = new HttpHeaders();

View file

@ -8,113 +8,75 @@
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall.CallType;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURIService;
@Lazy
@Service
@GuiProfile
public class RestService {
/** Interface to SEB Server webservice API thought RestCall's
* or thought Spring's RestTemplate on lower level.
*
* A RestCall's can be used to call a specified SEB Server webservice API endpoint with given request parameter.
* This service collects all the available RestCalls and map them by Class type or EntityType and CallType.
*
* For Example if one want to get a certain User-Account by API request on SEB Server webservice API:
*
* <pre>
* UserInfo userInfo = RestService.getBuilder(GetUserAccount.class)
* .withURIVariable(API.PARAM_MODEL_ID, user-account-id) adds an URI path variable
* .withQueryParam(API.PARAM_INSTITUTION_ID, institutionId) adds a URI query parameter
* .call() executes the API request call
* .get(pageContext::notifyError) gets the result or notify an error to the user if happened
* </pre>
*/
public interface RestService {
private final AuthorizationContextHolder authorizationContextHolder;
private final WebserviceURIService webserviceURIBuilderSupplier;
private final Map<String, RestCall<?>> calls;
/** Get Spring's RestTemplate that is used within this service.
*
* @return Spring's RestTemplate that is used within this service. */
RestTemplate getWebserviceAPIRestTemplate();
public RestService(
final AuthorizationContextHolder authorizationContextHolder,
final JSONMapper jsonMapper,
final Collection<RestCall<?>> calls) {
/** Get Spring's UriComponentsBuilder that is used within this service.
*
* @return Spring's UriComponentsBuilder that is used within this service. */
UriComponentsBuilder getWebserviceURIBuilder();
this.authorizationContextHolder = authorizationContextHolder;
this.webserviceURIBuilderSupplier = authorizationContextHolder
.getWebserviceURIService();
/** Get a certain RestCall by Class type.
*
* @param type the Class type of the RestCall
* @return RestCall instance */
<T> RestCall<T> getRestCall(Class<? extends RestCall<T>> type);
this.calls = calls
.stream()
.collect(Collectors.toMap(
call -> call.getClass().getName(),
call -> call.init(this, jsonMapper)));
}
/** Get a certain RestCall by EntityType and CallType.
* NOTE not all RestCall can be get within this method. Only the ones that have a defined CallType
*
* @param entityType The EntityType of the RestCall
* @param callType The CallType of the RestCall (not UNDEFINDED)
* @return RestCall instance */
<T> RestCall<T> getRestCall(EntityType entityType, CallType callType);
public final RestTemplate getWebserviceAPIRestTemplate() {
return this.authorizationContextHolder
.getAuthorizationContext()
.getRestTemplate();
}
/** Get a certain RestCallBuilder by RestCall Class type.
*
* @param type the Class type of the RestCall
* @return RestCallBuilder instance to build a dedicated call and execute it */
<T> RestCall<T>.RestCallBuilder getBuilder(Class<? extends RestCall<T>> type);
public final UriComponentsBuilder getWebserviceURIBuilder() {
return this.webserviceURIBuilderSupplier.getURIBuilder();
}
/** Get a certain RestCallBuilder by EntityType and CallType.
*
* @param entityType The EntityType of the RestCall to get a builder for
* @param callType The CallType of the RestCall to get a builder for (not UNDEFINDED)
* @return RestCallBuilder instance to build a dedicated call and execute it */
<T> RestCall<T>.RestCallBuilder getBuilder(
EntityType entityType,
CallType callType);
@SuppressWarnings("unchecked")
public final <T> RestCall<T> getRestCall(final Class<? extends RestCall<T>> type) {
return (RestCall<T>) this.calls.get(type.getName());
}
/** Performs an activation Action on RestCall specified within the given Action.
* The RestCall must be of CallType.ACTIVATION_ACTIVATE or CallType.ACTIVATION_DEACTIVATE
*
* @param action the Action that defines an entity activation
* @return the successfully executed Action */
<T> Action activation(Action action);
@SuppressWarnings("unchecked")
public final <T> RestCall<T> getRestCall(final EntityType entityType, final CallType callType) {
return (RestCall<T>) this.calls.values()
.stream()
.filter(call -> call.typeKey.callType == callType && call.typeKey.entityType == entityType)
.findFirst()
.orElse(null);
}
public final <T> RestCall<T>.RestCallBuilder getBuilder(final Class<? extends RestCall<T>> type) {
@SuppressWarnings("unchecked")
final RestCall<T> restCall = (RestCall<T>) this.calls.get(type.getName());
if (restCall == null) {
return null;
}
return restCall.newBuilder();
}
public final <T> RestCall<T>.RestCallBuilder getBuilder(
final EntityType entityType,
final CallType callType) {
final RestCall<T> restCall = getRestCall(entityType, callType);
if (restCall == null) {
return null;
}
return restCall.newBuilder();
}
public <T> Action activation(final Action action) {
if (action.definition.restCallType == null) {
throw new IllegalArgumentException("ActionDefinition needs to define a restCallType to use this action");
}
@SuppressWarnings("unchecked")
final Class<? extends RestCall<T>> restCallType =
(Class<? extends RestCall<T>>) action.definition.restCallType;
this.getBuilder(restCallType)
.withURIVariable(
API.PARAM_MODEL_ID,
action.pageContext().getAttribute(AttributeKeys.ENTITY_ID))
.call()
.onErrorDo(t -> action.pageContext().notifyError(t));
return action;
}
}
}

View file

@ -0,0 +1,136 @@
/*
* 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;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall.CallType;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURIService;
@Lazy
@Service
@GuiProfile
public class RestServiceImpl implements RestService {
private final AuthorizationContextHolder authorizationContextHolder;
private final WebserviceURIService webserviceURIBuilderSupplier;
private final Map<String, RestCall<?>> calls;
public RestServiceImpl(
final AuthorizationContextHolder authorizationContextHolder,
final JSONMapper jsonMapper,
final Collection<RestCall<?>> calls) {
this.authorizationContextHolder = authorizationContextHolder;
this.webserviceURIBuilderSupplier = authorizationContextHolder
.getWebserviceURIService();
this.calls = calls
.stream()
.collect(Collectors.toMap(
call -> call.getClass().getName(),
call -> call.init(this, jsonMapper)));
}
@Override
public final RestTemplate getWebserviceAPIRestTemplate() {
return this.authorizationContextHolder
.getAuthorizationContext()
.getRestTemplate();
}
@Override
public final UriComponentsBuilder getWebserviceURIBuilder() {
return this.webserviceURIBuilderSupplier.getURIBuilder();
}
@Override
@SuppressWarnings("unchecked")
public final <T> RestCall<T> getRestCall(final Class<? extends RestCall<T>> type) {
return (RestCall<T>) this.calls.get(type.getName());
}
@Override
@SuppressWarnings("unchecked")
public final <T> RestCall<T> getRestCall(final EntityType entityType, final CallType callType) {
if (callType == CallType.UNDEFINED) {
throw new IllegalArgumentException("Undefined CallType not supported");
}
return (RestCall<T>) this.calls.values()
.stream()
.filter(call -> call.typeKey.callType == callType && call.typeKey.entityType == entityType)
.findFirst()
.orElse(null);
}
@Override
public final <T> RestCall<T>.RestCallBuilder getBuilder(final Class<? extends RestCall<T>> type) {
@SuppressWarnings("unchecked")
final RestCall<T> restCall = (RestCall<T>) this.calls.get(type.getName());
if (restCall == null) {
return null;
}
return restCall.newBuilder();
}
@Override
public final <T> RestCall<T>.RestCallBuilder getBuilder(
final EntityType entityType,
final CallType callType) {
if (callType == CallType.UNDEFINED) {
throw new IllegalArgumentException("Undefined CallType not supported");
}
final RestCall<T> restCall = getRestCall(entityType, callType);
if (restCall == null) {
return null;
}
return restCall.newBuilder();
}
@Override
public <T> Action activation(final Action action) {
if (action.definition.restCallType == null) {
throw new IllegalArgumentException("ActionDefinition needs to define a restCallType to use this action");
}
@SuppressWarnings("unchecked")
final Class<? extends RestCall<T>> restCallType =
(Class<? extends RestCall<T>>) action.definition.restCallType;
this.getBuilder(restCallType)
.withURIVariable(
API.PARAM_MODEL_ID,
action.pageContext().getAttribute(AttributeKeys.ENTITY_ID))
.call()
.onErrorDo(t -> action.pageContext().notifyError(t));
return action;
}
}

View file

@ -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.exam;
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.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class ActivateExam extends RestCall<EntityProcessingReport> {
protected ActivateExam() {
super(new TypeKey<>(
CallType.ACTIVATION_ACTIVATE,
EntityType.EXAM,
new TypeReference<EntityProcessingReport>() {
}),
HttpMethod.POST,
MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_ADMINISTRATION_ENDPOINT + API.PATH_VAR_ACTIVE);
}
}

View file

@ -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.exam;
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.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class DeactivateExam extends RestCall<EntityProcessingReport> {
protected DeactivateExam() {
super(new TypeKey<>(
CallType.ACTIVATION_DEACTIVATE,
EntityType.EXAM,
new TypeReference<EntityProcessingReport>() {
}),
HttpMethod.POST,
MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_ADMINISTRATION_ENDPOINT + API.PATH_VAR_INACTIVE);
}
}

View file

@ -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.exam;
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.exam.Exam;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetExam extends RestCall<Exam> {
protected GetExam() {
super(new TypeKey<>(
CallType.GET_SINGLE,
EntityType.EXAM,
new TypeReference<Exam>() {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_ADMINISTRATION_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT);
}
}

View file

@ -0,0 +1,43 @@
/*
* 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.exam;
import java.util.Set;
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.EntityKey;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetExamDependencies extends RestCall<Set<EntityKey>> {
protected GetExamDependencies() {
super(new TypeKey<>(
CallType.GET_DEPENDENCIES,
EntityType.EXAM,
new TypeReference<Set<EntityKey>>() {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_ADMINISTRATION_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT
+ API.DEPENDENCY_PATH_SEGMENT);
}
}

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.exam;
import java.util.List;
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.EntityName;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetExamNames extends RestCall<List<EntityName>> {
protected GetExamNames() {
super(new TypeKey<>(
CallType.GET_NAMES,
EntityType.EXAM,
new TypeReference<List<EntityName>>() {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_ADMINISTRATION_ENDPOINT + API.NAMES_PATH_SEGMENT);
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam;
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.Page;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetExams extends RestCall<Page<Exam>> {
protected GetExams() {
super(new TypeKey<>(
CallType.GET_PAGE,
EntityType.EXAM,
new TypeReference<Page<Exam>>() {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_ADMINISTRATION_ENDPOINT);
}
}

View file

@ -0,0 +1,39 @@
/*
* 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.exam;
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.exam.Exam;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class SaveExam extends RestCall<Exam> {
protected SaveExam() {
super(new TypeKey<>(
CallType.SAVE,
EntityType.EXAM,
new TypeReference<Exam>() {
}),
HttpMethod.PUT,
MediaType.APPLICATION_JSON_UTF8,
API.EXAM_ADMINISTRATION_ENDPOINT);
}
}

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.lmssetup;
import java.util.List;
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.EntityName;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetLmsSetupNames extends RestCall<List<EntityName>> {
protected GetLmsSetupNames() {
super(new TypeKey<>(
CallType.GET_NAMES,
EntityType.LMS_SETUP,
new TypeReference<List<EntityName>>() {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.LMS_SETUP_ENDPOINT + API.NAMES_PATH_SEGMENT);
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz;
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.Page;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetQuizzes extends RestCall<Page<QuizData>> {
protected GetQuizzes() {
super(new TypeKey<>(
CallType.GET_PAGE,
EntityType.EXAM,
new TypeReference<Page<QuizData>>() {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.QUIZ_DISCOVERY_ENDPOINT);
}
}

View file

@ -0,0 +1,13 @@
/*
* 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.quiz;
public class ImportAsExam {
}

View file

@ -12,13 +12,28 @@ import javax.servlet.http.HttpSession;
import org.eclipse.rap.rwt.RWT;
/** Single point of access for SEBServerAuthorizationContext */
public interface AuthorizationContextHolder {
/** Get the SEBServerAuthorizationContext that is bound the the given HttpSession.
* If there is no AuthorizationContext or an invalid one within the given HttpSession
* a new one is created for the given HttpSession
*
* @param session HttpSession instance
* @return SEBServerAuthorizationContext instance */
SEBServerAuthorizationContext getAuthorizationContext(HttpSession session);
/** Get the WebserviceURIService that is used within this AuthorizationContextHolder
*
* @return the WebserviceURIService that is used within this AuthorizationContextHolder */
WebserviceURIService getWebserviceURIService();
// TODO error handling!?
/** Get the SEBServerAuthorizationContext that is bound the HttpSession of the current
* RWT UISession.
* NOTE: This may throw an exception if RWT.getUISession().getHttpSession() throws one
* This is the case if there is no RWT UISession within the current Thread
*
* @return SEBServerAuthorizationContext instance */
default SEBServerAuthorizationContext getAuthorizationContext() {
return getAuthorizationContext(RWT.getUISession().getHttpSession());
}

View file

@ -20,7 +20,6 @@ import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
import ch.ethz.seb.sebserver.gbl.model.user.ExamineeAccountDetails;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentials;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
@ -31,7 +30,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
*
* A LmsAPITemplate defines at least the core API access to query courses and quizzes from the LMS
* Later a concrete LmsAPITemplate may also implement some special features regarding to the type
* of LMS */
* of the LMS */
public interface LmsAPITemplate {
/** Get the underling LMSSetup configuration for this LmsAPITemplate
@ -54,9 +53,17 @@ public interface LmsAPITemplate {
* or refer to an error when happened */
Result<List<QuizData>> getQuizzes(FilterMap filterMap);
/** Get all QuizData for the set of QuizData identifiers from LMS API in a collection
* of Result. If particular Quiz cannot be loaded because of errors or deletion,
* the Result will have an error reference.
*
* @param ids the Set of Quiz identifiers to get the QuizData for
* @return Collection of all QuizData from the given id set */
Collection<Result<QuizData>> getQuizzes(Set<String> ids);
Result<ExamineeAccountDetails> getExamineeAccountDetails(String examineeUserId);
// TODO this can be used in a future release to resolve examinee's account detail information by an
// examinee identifier received by on SEB-Client connection.
//Result<ExamineeAccountDetails> getExamineeAccountDetails(String examineeUserId);
default List<APIMessage> attributeValidation(final ClientCredentials credentials) {

View file

@ -21,7 +21,6 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
import ch.ethz.seb.sebserver.gbl.model.user.ExamineeAccountDetails;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentialService;
import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentials;
@ -50,27 +49,28 @@ final class MockupLmsAPITemplate implements LmsAPITemplate {
this.clientCredentialService = clientCredentialService;
this.credentials = credentials;
final String lmsSetupId = lmsSetup.getModelId();
this.mockups = new ArrayList<>();
this.mockups.add(new QuizData(
"quiz1", "Demo Quiz 1", "Demo Quit Mockup",
"quiz1", lmsSetupId, "Demo Quiz 1", "Demo Quit Mockup",
"2020-01-01 09:00:00", "2021-01-01 09:00:00", "http://lms.mockup.com/api/"));
this.mockups.add(new QuizData(
"quiz2", "Demo Quiz 2", "Demo Quit Mockup",
"quiz2", lmsSetupId, "Demo Quiz 2", "Demo Quit Mockup",
"2020-01-01 09:00:00", "2021-01-01 09:00:00", "http://lms.mockup.com/api/"));
this.mockups.add(new QuizData(
"quiz3", "Demo Quiz 3", "Demo Quit Mockup",
"quiz3", lmsSetupId, "Demo Quiz 3", "Demo Quit Mockup",
"2018-07-30 09:00:00", "2018-08-01 00:00:00", "http://lms.mockup.com/api/"));
this.mockups.add(new QuizData(
"quiz4", "Demo Quiz 4", "Demo Quit Mockup",
"quiz4", lmsSetupId, "Demo Quiz 4", "Demo Quit Mockup",
"2018-01-01 00:00:00", "2019-01-01 00:00:00", "http://lms.mockup.com/api/"));
this.mockups.add(new QuizData(
"quiz5", "Demo Quiz 5", "Demo Quit Mockup",
"quiz5", lmsSetupId, "Demo Quiz 5", "Demo Quit Mockup",
"2018-01-01 09:00:00", "2021-01-01 09:00:00", "http://lms.mockup.com/api/"));
this.mockups.add(new QuizData(
"quiz6", "Demo Quiz 6", "Demo Quit Mockup",
"quiz6", lmsSetupId, "Demo Quiz 6", "Demo Quit Mockup",
"2018-01-01 09:00:00", "2021-01-01 09:00:00", "http://lms.mockup.com/api/"));
this.mockups.add(new QuizData(
"quiz7", "Demo Quiz 7", "Demo Quit Mockup",
"quiz7", lmsSetupId, "Demo Quiz 7", "Demo Quit Mockup",
"2018-01-01 09:00:00", "2021-01-01 09:00:00", "http://lms.mockup.com/api/"));
}
@ -124,16 +124,6 @@ final class MockupLmsAPITemplate implements LmsAPITemplate {
.collect(Collectors.toList());
}
@Override
public Result<ExamineeAccountDetails> getExamineeAccountDetails(final String examineeUserId) {
authenticate();
if (this.credentials == null) {
throw new IllegalArgumentException("Wrong clientId or secret");
}
return Result.of(new ExamineeAccountDetails(examineeUserId, "mockup", "mockup", "mockup"));
}
private boolean authenticate() {
try {

View file

@ -41,7 +41,6 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
import ch.ethz.seb.sebserver.gbl.model.user.ExamineeAccountDetails;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.SupplierWithCircuitBreaker;
import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentialService;
@ -136,12 +135,6 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
return null;
}
@Override
public Result<ExamineeAccountDetails> getExamineeAccountDetails(final String examineeUserId) {
// TODO Auto-generated method stub
return null;
}
private Result<LmsSetup> initRestTemplateAndRequestAccessToken() {
log.info("Initialize Rest Template for OpenEdX API access. LmsSetup: {}", this.lmsSetup);
@ -247,6 +240,7 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
final String startURI = lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_COURSE_START_URL_PREFIX + courseData.id;
return new QuizData(
courseData.id,
lmsSetup.getModelId(),
courseData.name,
courseData.short_description,
courseData.start,

View file

@ -34,6 +34,11 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
/** Abstract Entity-Controller that defines generic Entity rest API endpoints that are supported
* by all entity types that has activation feature and can be activated or deactivated.
*
* @param <T> The concrete Entity domain-model type used on all GET, PUT
* @param <M> The concrete Entity domain-model type used for POST methods (new) */
public abstract class ActivatableEntityController<T extends GrantEntity, M extends GrantEntity>
extends EntityController<T, M> {

View file

@ -50,6 +50,11 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
/** Abstract Entity-Controller that defines generic Entity rest API endpoints that are supported
* by all entity types.
*
* @param <T> The concrete Entity domain-model type used on all GET, PUT
* @param <M> The concrete Entity domain-model type used for POST methods (new) */
public abstract class EntityController<T extends GrantEntity, M extends GrantEntity> {
protected final AuthorizationService authorization;
@ -75,6 +80,11 @@ public abstract class EntityController<T extends GrantEntity, M extends GrantEnt
this.beanValidationService = beanValidationService;
}
/** This is called by Spring to initialize the WebDataBinder and is used here to
* initialize the default value binding for the institutionId request-parameter
* that has the current users insitutionId as default.
*
* See also UserService.addUsersInstitutionDefaultPropertySupport */
@InitBinder
public void initBinder(final WebDataBinder binder) throws Exception {
this.authorization
@ -83,14 +93,32 @@ public abstract class EntityController<T extends GrantEntity, M extends GrantEnt
}
// ******************
// * GET (all)
// * GET (getAll)
// ******************
/** The get-all or get-page rest endpoint for all types of Entity and returns a Page of
* entities of specific type.
*
* GET /{api}/{entity-type-endpoint-name}
*
* GET /admin-api/v1/exam
* GET /admin-api/v1/exam?page_number=2&page_size=10&sort=-name
* GET /admin-api/v1/exam?name=seb&active=true
*
* @param institutionId The institution identifier of the request.
* Default is the institution identifier of the institution of the current user
* @param pageNumber the number of the page that is requested
* @param pageSize the size of the page that is requested
* @param sort the sort parameter to sort the list of entities before paging
* the sort parameter is the name of the entity-model attribute to sort with a leading '-' sign for
* descending sort order
* @param allRequestParams a MultiValueMap of all request parameter that is used for filtering
* @return Page of domain-model-entities of specified type */
@RequestMapping(
method = RequestMethod.GET,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Page<T> getAll(
public Page<T> getPage(
@RequestParam(
name = API.PARAM_INSTITUTION_ID,
required = true,
@ -363,8 +391,6 @@ public abstract class EntityController<T extends GrantEntity, M extends GrantEnt
protected abstract M createNew(POSTMapper postParams);
protected abstract Class<M> modifiedDataType();
protected abstract SqlTable getSQLTableOfEntity();
}

View file

@ -87,11 +87,6 @@ public class ExamAdministrationController extends ActivatableEntityController<Ex
this.lmsAPIService = lmsAPIService;
}
@Override
protected Class<Exam> modifiedDataType() {
return Exam.class;
}
@Override
protected SqlTable getSQLTableOfEntity() {
return ExamRecordDynamicSqlSupport.examRecord;
@ -102,7 +97,7 @@ public class ExamAdministrationController extends ActivatableEntityController<Ex
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
@Override
public Page<Exam> getAll(
public Page<Exam> getPage(
@RequestParam(
name = API.PARAM_INSTITUTION_ID,
required = true,
@ -120,7 +115,7 @@ public class ExamAdministrationController extends ActivatableEntityController<Ex
if (StringUtils.isBlank(sort) ||
this.paginationService.isNativeSortingSupported(ExamRecordDynamicSqlSupport.examRecord, sort)) {
return super.getAll(institutionId, pageNumber, pageSize, sort, allRequestParams);
return super.getPage(institutionId, pageNumber, pageSize, sort, allRequestParams);
} else {

View file

@ -65,11 +65,6 @@ public class InstitutionController extends ActivatableEntityController<Instituti
this.sebClientConfigService = sebClientConfigService;
}
@Override
protected Class<Institution> modifiedDataType() {
return Institution.class;
}
@Override
protected SqlTable getSQLTableOfEntity() {
return InstitutionRecordDynamicSqlSupport.institutionRecord;

View file

@ -58,11 +58,6 @@ public class LmsSetupController extends ActivatableEntityController<LmsSetup, Lm
this.lmsAPIService = lmsAPIService;
}
@Override
protected Class<LmsSetup> modifiedDataType() {
return LmsSetup.class;
}
@Override
protected SqlTable getSQLTableOfEntity() {
return LmsSetupRecordDynamicSqlSupport.lmsSetupRecord;

View file

@ -9,6 +9,7 @@
package ch.ethz.seb.sebserver.webservice.weblayer.api;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@ -29,7 +30,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
@WebServiceProfile
@RestController
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.QUIZ_IMPORT_ENDPOINT)
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.QUIZ_DISCOVERY_ENDPOINT)
public class QuizImportController {
private final int defaultPageSize;
@ -50,8 +51,11 @@ public class QuizImportController {
this.authorization = authorization;
}
@RequestMapping(method = RequestMethod.GET)
public Page<QuizData> search(
@RequestMapping(
method = RequestMethod.GET,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Page<QuizData> getQuizPage(
@RequestParam(
name = Entity.FILTER_ATTR_INSTITUTION,
required = true,

View file

@ -90,11 +90,6 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
.getUserInfo();
}
@Override
protected Class<UserMod> modifiedDataType() {
return UserMod.class;
}
@Override
protected SqlTable getSQLTableOfEntity() {
return UserRecordDynamicSqlSupport.userRecord;

View file

@ -25,7 +25,7 @@ import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestServiceImpl;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.OAuth2AuthorizationContextHolder;
public class RestServiceTest extends GuiIntegrationTest {
@ -36,7 +36,7 @@ public class RestServiceTest extends GuiIntegrationTest {
final Collection<RestCall<?>> calls = new ArrayList<>();
calls.add(new RestServiceTest.GetInstitution());
final RestService restService = new RestService(authorizationContextHolder, new JSONMapper(), calls);
final RestServiceImpl restService = new RestServiceImpl(authorizationContextHolder, new JSONMapper(), calls);
final Result<Institution> call = restService.getBuilder(RestServiceTest.GetInstitution.class)
.withURIVariable(API.PARAM_MODEL_ID, "2")