API documentation

This commit is contained in:
anhefti 2021-05-11 21:07:57 +02:00
parent 10fd2f408d
commit 70fcbead41
2 changed files with 49 additions and 44 deletions

View file

@ -28,13 +28,23 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
/** Defines the interface to an LMS within a specified LMSSetup configuration. /** Defines an LMS API access template to build SEB Server LMS integration.
* There is one concrete implementations for every supported type of LMS like
* Open edX or Moodle
* *
* A LmsAPITemplate defines at least the core API access to query courses and quizzes from the LMS * A LMS integration consists of two main parts so far:
* Later a concrete LmsAPITemplate may also implement some special features regarding to the type * - The course API to search and request course data from LMS as well as resolve some LMS account details for a given
* of the LMS */ * examineeId
* - The SEB restriction API to apply SEB restriction data to the LMS to restrict a certain course for SEB
*
* A LmsAPITemplate is been constructed within a LmsSetup that defines the LMS setup data that is needed to connect to
* a specific LMS instance of implemented type.
*
* The enum LmsSetup.LmsType defines the supported LMS types and for each type the supported API part(s).
*
* SEB Server uses the test functions that are defined for each LMS API part to test API access for a certain LMS
* instance respectively the underling LMSSetup. Concrete implementations can do various tests to check full
* or partial API Access and can flag missing or wrong LMSSetup attributes with the resulting LmsSetupTestResult.
*
* SEB Server than uses an instance of this template to communicate with the an LMS. */
public interface LmsAPITemplate { public interface LmsAPITemplate {
/** Get the underling LMSSetup configuration for this LmsAPITemplate /** Get the underling LMSSetup configuration for this LmsAPITemplate
@ -56,7 +66,7 @@ public interface LmsAPITemplate {
* @return LmsSetupTestResult instance with the test result report */ * @return LmsSetupTestResult instance with the test result report */
LmsSetupTestResult testCourseRestrictionAPI(); LmsSetupTestResult testCourseRestrictionAPI();
/** Get a Result of an unsorted List of filtered QuizData from the LMS course/quiz API /** Get an unsorted List of filtered QuizData from the LMS course/quiz API
* *
* @param filterMap the FilterMap to get a filtered result. For possible filter attributes * @param filterMap the FilterMap to get a filtered result. For possible filter attributes
* see documentation on QuizData * see documentation on QuizData
@ -89,32 +99,35 @@ public interface LmsAPITemplate {
.orElse(Result.ofError(new ResourceNotFoundException(EntityType.EXAM, id))); .orElse(Result.ofError(new ResourceNotFoundException(EntityType.EXAM, id)));
} }
/** Get all QuizData for the set of QuizData identifiers from LMS API in a collection /** Get all QuizData for the set of QuizData-identifiers (ids) from the LMS defined within the
* of Result. If particular Quiz cannot be loaded because of errors or deletion, * underling LMSSetup, in a collection of Results.
* the Result will have an error reference.
* *
* NOTE: This method looks first in the cache if existing for all given ids. * If there is caching involved this function shall try to get the data from the cache first.
*
* NOTE: This function depends on the specific LMS implementation and on whether caching the quiz data
* makes sense or not. Following strategy is recommended:
* Looks first in the cache if the whole set of QuizData can be get from the cache.
* If all quizzes are cached, returns all from cache. * If all quizzes are cached, returns all from cache.
* If one quiz is not in the cache, requests all quizzes from the API and refreshes the cache * If one or more quiz is not in the cache, requests all quizzes from the API and refreshes the cache
* *
* @param ids the Set of Quiz identifiers to get the QuizData for * @param ids the Set of Quiz identifiers to get the QuizData for
* @return Collection of all QuizData from the given id set */ * @return Collection of all QuizData from the given id set */
Collection<Result<QuizData>> getQuizzesFromCache(Set<String> ids); Collection<Result<QuizData>> getQuizzesFromCache(Set<String> ids);
/** Convert a an anonymous or temporary user session identifier from SEB Client into a user /** Convert an anonymous or temporary examineeUserId, sent by the SEB Client on LMS login,
* account details. * to LMS examinee account details by requesting them on the LMS API with the given examineeUserId
* *
* @param examineeUserId the user-account identifier derived from SEB Client * @param examineeUserId the examinee user identifier derived from SEB Client
* @return a Result refer to the ExamineeAccountDetails instance or to an error when happened or not supported */ * @return a Result refer to the ExamineeAccountDetails instance or to an error when happened or not supported */
Result<ExamineeAccountDetails> getExamineeAccountDetails(String examineeUserId); Result<ExamineeAccountDetails> getExamineeAccountDetails(String examineeUserId);
/** Used to convert an anonymous or temporary user session identifier from SEB Client into a user /** Used to convert an anonymous or temporary examineeUserId, sent by the SEB Client on LMS login,
* account name for displaying on monitoring page. * to a readable LMS examinee account name by requesting this on the LMS API with the given examineeUserId.
* *
* If the underling concrete template implementation does not support this user name conversion, * If the underling concrete template implementation does not support this user name conversion,
* the given examineeSessionId shall be returned. * the given examineeSessionId shall be returned.
* *
* @param examineeUserId the user-account identifier derived from SEB Client * @param examineeUserId the examinee user identifier derived from SEB Client
* @return a user account display name if supported or the given examineeSessionId if not. */ * @return a user account display name if supported or the given examineeSessionId if not. */
String getExamineeName(String examineeUserId); String getExamineeName(String examineeUserId);
@ -129,18 +142,18 @@ public interface LmsAPITemplate {
* @return Result referencing to the Chapters model for the given course or to an error when happened. */ * @return Result referencing to the Chapters model for the given course or to an error when happened. */
Result<Chapters> getCourseChapters(String courseId); Result<Chapters> getCourseChapters(String courseId);
/** Get SEB restriction data form LMS within a SEBRestrictionData instance if available /** Get SEB restriction data form LMS within a SEBRestrictionData instance. The available restriction details
* or a ResourceNotFoundException if not yet available or restricted * depends on the type of LMS but shall at least contains the config-key(s) and the browser-exam-key(s).
* *
* @param exam the exam to get the SEB restriction data for * @param exam the exam to get the SEB restriction data for
* @return Result refer to the SEBRestrictionData instance or to an ResourceNotFoundException if restriction is * @return Result refer to the SEBRestrictionData instance or to an ResourceNotFoundException if the restriction is
* missing or to another exception on unexpected error case */ * missing or to another exception on unexpected error case */
Result<SEBRestriction> getSEBClientRestriction(Exam exam); Result<SEBRestriction> getSEBClientRestriction(Exam exam);
/** Applies a SEB Client restriction within the LMS with the given attributes. /** Applies SEB Client restrictions to the LMS with the given attributes.
* *
* @param externalExamId The exam identifier from LMS side (Exam.externalId) * @param externalExamId The exam/course identifier from LMS side (Exam.externalId)
* @param sebRestrictionData containing all data for SEB Client restriction * @param sebRestrictionData containing all data for SEB Client restriction to apply to the LMS
* @return Result refer to the given SEBRestrictionData if restriction was successful or to an error if not */ * @return Result refer to the given SEBRestrictionData if restriction was successful or to an error if not */
Result<SEBRestriction> applySEBClientRestriction( Result<SEBRestriction> applySEBClientRestriction(
String externalExamId, String externalExamId,
@ -149,7 +162,7 @@ public interface LmsAPITemplate {
/** Releases an already applied SEB Client restriction within the LMS for a given Exam. /** Releases an already applied SEB Client restriction within the LMS for a given Exam.
* This completely removes the SEB Client restriction on LMS side. * This completely removes the SEB Client restriction on LMS side.
* *
* @param exam the Exam to release the restriction for * @param exam the Exam to release the restriction for.
* @return Result refer to the given Exam if successful or to an error if not */ * @return Result refer to the given Exam if successful or to an error if not */
Result<Exam> releaseSEBClientRestriction(Exam exam); Result<Exam> releaseSEBClientRestriction(Exam exam);

View file

@ -296,21 +296,7 @@ public class MoodleCourseAccess extends CourseAccess {
return Collections.emptyList(); return Collections.emptyList();
} }
return courseQuizData return reduceCoursesToQuizzes(urlPrefix, courseQuizData);
.stream()
.reduce(
new ArrayList<>(),
(list, courseData) -> {
list.addAll(quizDataOf(
this.lmsSetup,
courseData,
urlPrefix));
return list;
},
(list1, list2) -> {
list1.addAll(list2);
return list1;
});
} }
private List<QuizData> getCached() { private List<QuizData> getCached() {
@ -319,6 +305,14 @@ public class MoodleCourseAccess extends CourseAccess {
final String urlPrefix = (this.lmsSetup.lmsApiUrl.endsWith(Constants.URL_PATH_SEPARATOR)) final String urlPrefix = (this.lmsSetup.lmsApiUrl.endsWith(Constants.URL_PATH_SEPARATOR))
? this.lmsSetup.lmsApiUrl + MOODLE_QUIZ_START_URL_PATH ? this.lmsSetup.lmsApiUrl + MOODLE_QUIZ_START_URL_PATH
: this.lmsSetup.lmsApiUrl + Constants.URL_PATH_SEPARATOR + MOODLE_QUIZ_START_URL_PATH; : this.lmsSetup.lmsApiUrl + Constants.URL_PATH_SEPARATOR + MOODLE_QUIZ_START_URL_PATH;
return reduceCoursesToQuizzes(urlPrefix, courseQuizData);
}
private ArrayList<QuizData> reduceCoursesToQuizzes(
final String urlPrefix,
final Collection<CourseDataShort> courseQuizData) {
return courseQuizData return courseQuizData
.stream() .stream()
.reduce( .reduce(
@ -465,14 +459,12 @@ public class MoodleCourseAccess extends CourseAccess {
} }
} }
static Map<String, String> additionalAttrs = new HashMap<>();
private List<QuizData> quizDataOf( private List<QuizData> quizDataOf(
final LmsSetup lmsSetup, final LmsSetup lmsSetup,
final CourseData courseData, final CourseData courseData,
final String uriPrefix) { final String uriPrefix) {
additionalAttrs.clear(); final Map<String, String> additionalAttrs = new HashMap<>();
additionalAttrs.put(QuizData.ATTR_ADDITIONAL_CREATION_TIME, String.valueOf(courseData.time_created)); additionalAttrs.put(QuizData.ATTR_ADDITIONAL_CREATION_TIME, String.valueOf(courseData.time_created));
additionalAttrs.put(QuizData.ATTR_ADDITIONAL_SHORT_NAME, courseData.short_name); additionalAttrs.put(QuizData.ATTR_ADDITIONAL_SHORT_NAME, courseData.short_name);
additionalAttrs.put(QuizData.ATTR_ADDITIONAL_ID_NUMBER, courseData.idnumber); additionalAttrs.put(QuizData.ATTR_ADDITIONAL_ID_NUMBER, courseData.idnumber);
@ -515,7 +507,7 @@ public class MoodleCourseAccess extends CourseAccess {
final CourseDataShort courseData, final CourseDataShort courseData,
final String uriPrefix) { final String uriPrefix) {
additionalAttrs.clear(); final Map<String, String> additionalAttrs = new HashMap<>();
additionalAttrs.put(QuizData.ATTR_ADDITIONAL_CREATION_TIME, String.valueOf(courseData.time_created)); additionalAttrs.put(QuizData.ATTR_ADDITIONAL_CREATION_TIME, String.valueOf(courseData.time_created));
additionalAttrs.put(QuizData.ATTR_ADDITIONAL_SHORT_NAME, courseData.short_name); additionalAttrs.put(QuizData.ATTR_ADDITIONAL_SHORT_NAME, courseData.short_name);
additionalAttrs.put(QuizData.ATTR_ADDITIONAL_ID_NUMBER, courseData.idnumber); additionalAttrs.put(QuizData.ATTR_ADDITIONAL_ID_NUMBER, courseData.idnumber);