SEBSERV-75 error handling for SEB Restriction
This commit is contained in:
parent
017d191e31
commit
6f9daddf53
9 changed files with 122 additions and 21 deletions
|
@ -46,7 +46,7 @@ public final class LmsSetup implements GrantEntity, Activatable {
|
|||
public enum LmsType {
|
||||
MOCKUP(Features.COURSE_API),
|
||||
OPEN_EDX(Features.COURSE_API, Features.SEB_RESTRICTION),
|
||||
MOODLE(Features.COURSE_API);
|
||||
MOODLE(Features.COURSE_API, Features.SEB_RESTRICTION);
|
||||
|
||||
public final EnumSet<Features> features;
|
||||
|
||||
|
|
|
@ -43,7 +43,8 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus;
|
|||
import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.Features;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult.ErrorType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
@ -73,6 +74,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamConfi
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorPage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetup;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.TestLmsSetup;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizData;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.ImportAsExam;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
||||
|
@ -620,12 +622,17 @@ public class ExamForm implements TemplateComposer {
|
|||
}
|
||||
|
||||
private boolean testSEBRestrictionAPI(final Exam exam) {
|
||||
return this.restService.getBuilder(GetLmsSetup.class)
|
||||
// Call the testing endpoint with the specified data to test
|
||||
final Result<LmsSetupTestResult> result = this.restService.getBuilder(TestLmsSetup.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(exam.lmsSetupId))
|
||||
.call()
|
||||
.onError(t -> log.error("Failed to check SEB restriction API: ", t))
|
||||
.map(lmsSetup -> lmsSetup.lmsType.features.contains(Features.SEB_RESTRICTION))
|
||||
.getOr(false);
|
||||
.call();
|
||||
|
||||
if (result.hasError()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final LmsSetupTestResult lmsSetupTestResult = result.get();
|
||||
return !lmsSetupTestResult.hasError(ErrorType.QUIZ_RESTRICTION_API_REQUEST);
|
||||
}
|
||||
|
||||
private void showConsistencyChecks(final Collection<APIMessage> result, final Composite parent) {
|
||||
|
|
|
@ -200,12 +200,13 @@ public class ExamSEBRestrictionSettings {
|
|||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
final Chapters chapters = restService
|
||||
.getBuilder(GetCourseChapters.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
.call()
|
||||
.onError(t -> t.printStackTrace())
|
||||
.getOr(null);
|
||||
final Chapters chapters = (lmsType == LmsType.OPEN_EDX)
|
||||
? restService.getBuilder(GetCourseChapters.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
.call()
|
||||
.onError(t -> t.printStackTrace())
|
||||
.getOr(null)
|
||||
: null;
|
||||
|
||||
final PageContext formContext = this.pageContext
|
||||
.copyOf(content)
|
||||
|
|
|
@ -457,9 +457,11 @@ public class LmsSetupForm implements TemplateComposer {
|
|||
Utils.formatHTMLLinesForceEscaped(Utils.escapeHTML_XML_EcmaScript(error.message))));
|
||||
}
|
||||
case QUIZ_RESTRICTION_API_REQUEST: {
|
||||
// NOTE: quiz restriction is not mandatory for functional LmsSetup
|
||||
// so this error is ignored here
|
||||
break;
|
||||
|
||||
throw new PageMessageException(new LocTextKey(
|
||||
"sebserver.lmssetup.action.test.features.error",
|
||||
"OK",
|
||||
Utils.formatHTMLLinesForceEscaped(Utils.escapeHTML_XML_EcmaScript(error.message))));
|
||||
}
|
||||
default: {
|
||||
throw new PageMessageException(new LocTextKey(
|
||||
|
|
|
@ -19,4 +19,8 @@ public class NoSEBRestrictionException extends RuntimeException {
|
|||
super(cause);
|
||||
}
|
||||
|
||||
public NoSEBRestrictionException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,12 +16,16 @@ import org.slf4j.LoggerFactory;
|
|||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.MoodleSEBRestriction;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.NoSEBRestrictionException;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory.MoodleAPIRestTemplate;
|
||||
|
||||
/** GET:
|
||||
|
@ -80,8 +84,27 @@ public class MoodleCourseRestriction {
|
|||
}
|
||||
|
||||
LmsSetupTestResult initAPIAccess() {
|
||||
// TODO test availability
|
||||
return LmsSetupTestResult.ofQuizRestrictionAPIError("not available yet");
|
||||
// try to call the SEB Restrictions API
|
||||
try {
|
||||
|
||||
final MoodleAPIRestTemplate template = getRestTemplate()
|
||||
.getOrThrow();
|
||||
|
||||
final String jsonResponse = template.callMoodleAPIFunction(
|
||||
MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION,
|
||||
new LinkedMultiValueMap<>(),
|
||||
null);
|
||||
|
||||
final Error checkError = this.checkError(jsonResponse);
|
||||
if (checkError != null) {
|
||||
return LmsSetupTestResult.ofQuizRestrictionAPIError(checkError.exception);
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.debug("Moodle SEB restriction API not available: ", e);
|
||||
return LmsSetupTestResult.ofQuizRestrictionAPIError(e.getMessage());
|
||||
}
|
||||
return LmsSetupTestResult.ofOkay();
|
||||
}
|
||||
|
||||
Result<MoodleSEBRestriction> getSEBRestriction(
|
||||
|
@ -125,6 +148,12 @@ public class MoodleCourseRestriction {
|
|||
queryParams,
|
||||
null);
|
||||
|
||||
final Error error = this.checkError(resultJSON);
|
||||
if (error != null) {
|
||||
log.error("Failed to get SEB restriction: {}", error.toString());
|
||||
throw new NoSEBRestrictionException("Failed to get SEB restriction: " + error.exception);
|
||||
}
|
||||
|
||||
final MoodleSEBRestriction restrictiondata = this.jsonMapper.readValue(
|
||||
resultJSON,
|
||||
new TypeReference<MoodleSEBRestriction>() {
|
||||
|
@ -241,11 +270,17 @@ public class MoodleCourseRestriction {
|
|||
queryParams.add(MOODLE_DEFAULT_COURSE_RESTRICTION_COURSE_ID, courseId);
|
||||
queryParams.add(MOODLE_DEFAULT_COURSE_RESTRICTION_QUIZ_ID, quizId);
|
||||
|
||||
template.callMoodleAPIFunction(
|
||||
final String jsonResponse = template.callMoodleAPIFunction(
|
||||
MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION_DELETE,
|
||||
queryParams,
|
||||
null);
|
||||
|
||||
final Error error = this.checkError(jsonResponse);
|
||||
if (error != null) {
|
||||
log.error("Failed to delete SEB restriction: {}", error.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
@ -291,6 +326,12 @@ public class MoodleCourseRestriction {
|
|||
queryParams,
|
||||
queryAttributes);
|
||||
|
||||
final Error error = this.checkError(resultJSON);
|
||||
if (error != null) {
|
||||
log.error("Failed to post SEB restriction: {}", error.toString());
|
||||
throw new NoSEBRestrictionException("Failed to post SEB restriction: " + error.exception);
|
||||
}
|
||||
|
||||
final MoodleSEBRestriction restrictiondata = this.jsonMapper.readValue(
|
||||
resultJSON,
|
||||
new TypeReference<MoodleSEBRestriction>() {
|
||||
|
@ -300,4 +341,50 @@ public class MoodleCourseRestriction {
|
|||
});
|
||||
}
|
||||
|
||||
public Error checkError(final String jsonResponse) {
|
||||
if (jsonResponse.contains("exception") || jsonResponse.contains("errorcode")) {
|
||||
try {
|
||||
return this.jsonMapper.readValue(
|
||||
jsonResponse,
|
||||
new TypeReference<Error>() {
|
||||
});
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to parse error response: {} cause: ", jsonResponse, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
private static class Error {
|
||||
public final String exception;
|
||||
public final String errorcode;
|
||||
public final String message;
|
||||
|
||||
@JsonCreator
|
||||
Error(
|
||||
@JsonProperty(value = "exception") final String exception,
|
||||
@JsonProperty(value = "errorcode") final String errorcode,
|
||||
@JsonProperty(value = "message") final String message) {
|
||||
this.exception = exception;
|
||||
this.errorcode = errorcode;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("Error [exception=");
|
||||
builder.append(this.exception);
|
||||
builder.append(", errorcode=");
|
||||
builder.append(this.errorcode);
|
||||
builder.append(", message=");
|
||||
builder.append(this.message);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ public class MoodleLmsAPITemplate implements LmsAPITemplate {
|
|||
|
||||
@Override
|
||||
public LmsSetupTestResult testCourseRestrictionAPI() {
|
||||
return LmsSetupTestResult.ofQuizRestrictionAPIError("Not available yet");
|
||||
return this.moodleCourseRestriction.initAPIAccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -307,6 +307,7 @@ sebserver.lmssetup.action.test.ok=Successfully connected to the course API
|
|||
sebserver.lmssetup.action.test.tokenRequestError=The API access was denied:<br/>{0}<br/><br/>Please check the LMS connection details.
|
||||
sebserver.lmssetup.action.test.quizRequestError=Unable to request courses or exams from the course API of the LMS. {0}
|
||||
sebserver.lmssetup.action.test.quizRestrictionError=Unable to access course restriction API of the LMS. {0}
|
||||
sebserver.lmssetup.action.test.features.error=The API access was granted but there is some missing functionality:<br/><br/>- Course Access: {0}<br/>- SEB Restriction: {1}
|
||||
sebserver.lmssetup.action.test.missingParameter=There is one or more missing connection parameter.<br/>Please check the connection parameter for this LMS Setup
|
||||
sebserver.lmssetup.action.test.unknownError=An unexpected error happened while trying to connect to the LMS course API. {0}
|
||||
sebserver.lmssetup.action.save=Save LMS Setup
|
||||
|
|
|
@ -152,7 +152,6 @@ public class MoodleCourseAccessTest {
|
|||
final MoodleRestTemplateFactory moodleRestTemplateFactory = mock(MoodleRestTemplateFactory.class);
|
||||
final MoodleAPIRestTemplate moodleAPIRestTemplate = mock(MoodleAPIRestTemplate.class);
|
||||
when(moodleRestTemplateFactory.createRestTemplate()).thenReturn(Result.of(moodleAPIRestTemplate));
|
||||
//doThrow(RuntimeException.class).when(moodleAPIRestTemplate).testAPIConnection(any());
|
||||
when(moodleRestTemplateFactory.test()).thenReturn(LmsSetupTestResult.ofOkay());
|
||||
|
||||
final MoodleCourseAccess moodleCourseAccess = new MoodleCourseAccess(
|
||||
|
|
Loading…
Add table
Reference in a new issue