diff --git a/.github/workflows/buildReporting.yml b/.github/workflows/buildReporting.yml index 87151c1e..700dfb38 100644 --- a/.github/workflows/buildReporting.yml +++ b/.github/workflows/buildReporting.yml @@ -1,143 +1,143 @@ -# This workflow will build a Java project with Maven -# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven - -name: build - -on: - push: - branches: - - '**' - tags: - - '**' - pull_request: - branches: [master, development] - -jobs: - maven-build-reporting: - runs-on: ubuntu-latest - steps: - - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Set up JDK 8 - uses: actions/setup-java@v2 - with: - java-version: '8' - distribution: 'adopt' - - - name: Cache Maven packages - uses: actions/cache@v2 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2 - - - name: Build with Maven - run: mvn clean install -e -P let_reporting - - - name: Reporting - uses: codecov/codecov-action@v1 - with: - flags: unittests - name: SEB Server Build - fail_ci_if_error: false - verbose: false - - maven-build-docker: - needs: maven-build-reporting - # Run only on tagging - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - runs-on: ubuntu-latest - steps: - - - name: Get short SHA - uses: benjlevesque/short-sha@v1.2 - id: short-sha - - - name: Store short SHA as environment variable - run: echo $SHA - env: - SHA: ${{ steps.short-sha.outputs.sha }} - - - name: Set env - run: echo "TAG_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - - - name: Test tag name - run: | - echo $TAG_NAME - echo ${{ env.TAG_NAME }} - - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Set up JDK 11 - uses: actions/setup-java@v2 - with: - java-version: '11' - distribution: 'adopt' - - - name: Cache Maven packages - uses: actions/cache@v2 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2 - - - name: Build with Maven - run: mvn clean install -Dmaven.test.skip=true -Dsebserver-version="${{ env.TAG_NAME }}-${{ env.SHA }}" - env: - sebserver-version: ${{ env.TAG_NAME }}-${{ env.SHA }} - - - name: Simplify package name - run: mv target/seb-server-${{ env.TAG_NAME }}-${{ env.SHA }}.jar target/seb-server.jar - - - uses: actions/upload-artifact@v2 - with: - name: Package - path: target/seb-server.jar - - docker-build: - needs: maven-build-docker - # Run only on tagging - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - runs-on: ubuntu-latest - steps: - - - name: Set env - run: echo "TAG_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - - - name: Test - run: | - echo $TAG_NAME - echo ${{ env.TAG_NAME }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Download a single artifact - uses: actions/download-artifact@v2 - with: - name: Package - - - name: Build and push - id: docker_build - uses: docker/build-push-action@v2 - with: - context: . - file: ./docker/Dockerfile - push: true - tags: | +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: build + +on: + push: + branches: + - '**' + tags: + - '**' + pull_request: + branches: [master, development] + +jobs: + maven-build-reporting: + runs-on: ubuntu-latest + steps: + - + name: Checkout repository + uses: actions/checkout@v2 + - + name: Set up JDK 8 + uses: actions/setup-java@v2 + with: + java-version: '8' + distribution: 'adopt' + - + name: Cache Maven packages + uses: actions/cache@v2 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + - + name: Build with Maven + run: mvn clean install -e -P let_reporting + - + name: Reporting + uses: codecov/codecov-action@v1 + with: + flags: unittests + name: SEB Server Build + fail_ci_if_error: false + verbose: false + + maven-build-docker: + needs: maven-build-reporting + # Run only on tagging + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - + name: Get short SHA + uses: benjlevesque/short-sha@v1.2 + id: short-sha + - + name: Store short SHA as environment variable + run: echo $SHA + env: + SHA: ${{ steps.short-sha.outputs.sha }} + - + name: Set env + run: echo "TAG_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + - + name: Test tag name + run: | + echo $TAG_NAME + echo ${{ env.TAG_NAME }} + - + name: Checkout repository + uses: actions/checkout@v2 + - + name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + - + name: Cache Maven packages + uses: actions/cache@v2 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + - + name: Build with Maven + run: mvn clean install -Dmaven.test.skip=true -Dsebserver-version="${{ env.TAG_NAME }}-${{ env.SHA }}" + env: + sebserver-version: ${{ env.TAG_NAME }}-${{ env.SHA }} + - + name: Simplify package name + run: mv target/seb-server-${{ env.TAG_NAME }}-${{ env.SHA }}.jar target/seb-server.jar + - + uses: actions/upload-artifact@v2 + with: + name: Package + path: target/seb-server.jar + + docker-build: + needs: maven-build-docker + # Run only on tagging + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - + name: Set env + run: echo "TAG_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + - + name: Test + run: | + echo $TAG_NAME + echo ${{ env.TAG_NAME }} + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Checkout repository + uses: actions/checkout@v2 + - + name: Download a single artifact + uses: actions/download-artifact@v2 + with: + name: Package + - + name: Build and push + id: docker_build + uses: docker/build-push-action@v2 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: | anhefti/seb-server:${{ env.TAG_NAME }} \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile index dddd0e39..0814052d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -22,27 +22,20 @@ pipeline { stage('Reporting') { steps { - pmd canComputeNew: false, defaultEncoding: '', healthy: '', pattern: '**/target/pmd.xml', thresholdLimit: 'high', unHealthy: '' - findbugs canComputeNew: false, defaultEncoding: '', excludePattern: '', healthy: '', includePattern: '', isRankActivated: true, pattern: '**/target/findbugsXml.xml', unHealthy: '' - jacoco classPattern: '**/build/classes/*/main/', execPattern: '**/target/*.exec', sourcePattern: '**/src/main/java', inclusionPattern: '**/*.class' - } - } - - stage('Tag') { - steps { - echo 'Build is tagged here.' + withMaven(maven: 'Maven', options: [findbugsPublisher(disabled: true)]) { + sh "mvn --batch-mode -V -U -e -P let_reporting pmd:pmd pmd:cpd findbugs:findbugs spotbugs:spotbugs" + } } } - - stage('Push to Nexus') { - steps { - echo 'Build is pushed to Nexus here.' - } - } - } post { + always { + junit testResults: '**/target/surefire-reports/TEST-*.xml' + + recordIssues enabledForFailure: true, tool: spotBugs() + recordIssues enabledForFailure: true, tool: pmdParser(pattern: '**/target/pmd.xml') + } failure { setBuildStatus("Build failed", "FAILURE"); emailext body: "The build of the LET Application (${env.JOB_NAME}) failed! See ${env.BUILD_URL}", recipientProviders: [[$class: 'CulpritsRecipientProvider']], subject: 'LET Application Build Failure' diff --git a/codecov.yml b/codecov.yml index 7f5dad09..818b3cf4 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,7 +1,7 @@ coverage: precision: 2 round: down - range: "40...100" + range: "30..70" status: project: default: diff --git a/docs/exam.rst b/docs/exam.rst index cd8a7190..39445f0b 100644 --- a/docs/exam.rst +++ b/docs/exam.rst @@ -151,6 +151,29 @@ your institution use the type information of the exam to set them into context. - When you have selected a exam configuration the dialog shows you some additional information about the exam configuration. - If you want or need to put an password protected encryption to the exam configuration for this exam you can do so by give the password for the encryption also within the attachment dialog. Be aware that every SEB client that will receive an encrypted exam configuration from the SEB Server will prompt the user to give the correct password. In most cases an encryption of the exam configuration is not needed, because a secure HTTPS connection form SEB client to SEB Server is already in place. +**Archive an exam** + +Since SEB Server version 1.4 it is possible to archive an exam that has been finished. An archived exam and all its data is still available +on the SEB Server but read only and the exam is not been updated from the LMS data anymore and it is not possible to run this exam again. + +This is a good use-case to organize your exams since archived exam are not shown in the Exam list with the default filter anymore. They are +only shown if the status filter of the exam list is explicitly set to Archived status. An they are shown within the new "Finished Exam" +section in the monitoring view. + +.. image:: images/exam/archiveExamsFilter.png + :align: center + :target: https://raw.githubusercontent.com/SafeExamBrowser/seb-server/master/docs/images/exam/archiveExamsFilter.png + +This is also a good use-case if you want to remove an LMS and LMS Setup but still want to be able to access the exams data on the SEB Server. +In this case you can archive all exams from that LMS Setup before deactivating or deleting the respective LMS Setup. + +To archive a finished exam you just have to use the "Archive Exam" action on the right action pane of the exam view: + +.. image:: images/exam/archiveExam1.png + :align: center + :target: https://raw.githubusercontent.com/SafeExamBrowser/seb-server/master/docs/images/exam/archiveExam1.png + + **Delete an exam** If you have "Exam Administrator" privileges you are able to entirely delete an existing exam and its dependencies. diff --git a/docs/exam_template.rst b/docs/exam_template.rst index 5dd6a909..cac22b9e 100644 --- a/docs/exam_template.rst +++ b/docs/exam_template.rst @@ -55,6 +55,14 @@ And you are able to add/edit/remove monitoring indicators for the exam template .. image:: images/exam_template/indicator.png :align: center :target: https://raw.githubusercontent.com/SafeExamBrowser/seb-server/master/docs/images/exam_template/indicator.png + +There are also proctoring settings available since SEB Server version 1.4 for the exam template. They just have the same settings and +look like the ones on the Exam and will get copied for an exam imported with the respective template that defines the proctoring settings. + +.. image:: images/exam_template/proctoringSettings.png + :align: center + :target: https://raw.githubusercontent.com/SafeExamBrowser/seb-server/master/docs/images/exam_template/proctoringSettings.png + Import Exam with Template ------------------------- diff --git a/docs/images/exam/archiveExam1.png b/docs/images/exam/archiveExam1.png new file mode 100644 index 00000000..e2f6d699 Binary files /dev/null and b/docs/images/exam/archiveExam1.png differ diff --git a/docs/images/exam/archiveExamsFilter.png b/docs/images/exam/archiveExamsFilter.png new file mode 100644 index 00000000..8aeaaba6 Binary files /dev/null and b/docs/images/exam/archiveExamsFilter.png differ diff --git a/docs/images/exam_config/batch-actions.png b/docs/images/exam_config/batch-actions.png new file mode 100644 index 00000000..f82f3fb3 Binary files /dev/null and b/docs/images/exam_config/batch-actions.png differ diff --git a/docs/images/exam_config/batch-actions_statechange.png b/docs/images/exam_config/batch-actions_statechange.png new file mode 100644 index 00000000..31912836 Binary files /dev/null and b/docs/images/exam_config/batch-actions_statechange.png differ diff --git a/docs/images/exam_config/batch-actions_statechange_finished.png b/docs/images/exam_config/batch-actions_statechange_finished.png new file mode 100644 index 00000000..adae9c7d Binary files /dev/null and b/docs/images/exam_config/batch-actions_statechange_finished.png differ diff --git a/docs/images/exam_config/bulkActionSelection1.png b/docs/images/exam_config/bulkActionSelection1.png new file mode 100644 index 00000000..d0c2f2a9 Binary files /dev/null and b/docs/images/exam_config/bulkActionSelection1.png differ diff --git a/docs/images/exam_config/bulkActionSelection2.png b/docs/images/exam_config/bulkActionSelection2.png new file mode 100644 index 00000000..2f1913e0 Binary files /dev/null and b/docs/images/exam_config/bulkActionSelection2.png differ diff --git a/docs/images/exam_config/bulkStateChange1.png b/docs/images/exam_config/bulkStateChange1.png new file mode 100644 index 00000000..6d84fd4e Binary files /dev/null and b/docs/images/exam_config/bulkStateChange1.png differ diff --git a/docs/images/exam_config/bulkStateChange2.png b/docs/images/exam_config/bulkStateChange2.png new file mode 100644 index 00000000..7e4de905 Binary files /dev/null and b/docs/images/exam_config/bulkStateChange2.png differ diff --git a/docs/images/exam_config/bulkStateChange3.png b/docs/images/exam_config/bulkStateChange3.png new file mode 100644 index 00000000..40ffcbcf Binary files /dev/null and b/docs/images/exam_config/bulkStateChange3.png differ diff --git a/docs/images/exam_config/reset_to_template.png b/docs/images/exam_config/reset_to_template.png new file mode 100644 index 00000000..c25a4cc6 Binary files /dev/null and b/docs/images/exam_config/reset_to_template.png differ diff --git a/docs/images/exam_template/proctoringSettings.png b/docs/images/exam_template/proctoringSettings.png new file mode 100644 index 00000000..cd170bb5 Binary files /dev/null and b/docs/images/exam_template/proctoringSettings.png differ diff --git a/docs/images/monitoring/finishedClientConnection.png b/docs/images/monitoring/finishedClientConnection.png new file mode 100644 index 00000000..52c4226b Binary files /dev/null and b/docs/images/monitoring/finishedClientConnection.png differ diff --git a/docs/images/monitoring/finishedExam.png b/docs/images/monitoring/finishedExam.png new file mode 100644 index 00000000..aa12674d Binary files /dev/null and b/docs/images/monitoring/finishedExam.png differ diff --git a/docs/images/monitoring/finishedExams.png b/docs/images/monitoring/finishedExams.png new file mode 100644 index 00000000..535a3125 Binary files /dev/null and b/docs/images/monitoring/finishedExams.png differ diff --git a/docs/images/overview/list_multiselect.png b/docs/images/overview/list_multiselect.png new file mode 100644 index 00000000..bdb70076 Binary files /dev/null and b/docs/images/overview/list_multiselect.png differ diff --git a/docs/images/overview/list_multiselect_actions.png b/docs/images/overview/list_multiselect_actions.png new file mode 100644 index 00000000..ee0d93ae Binary files /dev/null and b/docs/images/overview/list_multiselect_actions.png differ diff --git a/docs/monitoring.rst b/docs/monitoring.rst index 6eefa4ed..fe280955 100644 --- a/docs/monitoring.rst +++ b/docs/monitoring.rst @@ -185,7 +185,39 @@ A student as well as a proctor is then able to use all the features of the meeti - In Zoom it is not possible to fully control a participant microphone. Therefore it may happen that participant can hear each other even if no proctor is in the meeting. - Within Jitsi Meet service when a proctor leaves the room it currently happens that a random participant became host/moderator since it is not possible in Jitsi Meet to have a meeting without host. We try to mitigate the problem with the `moderator plugin `_ or `Jitsi Meet SaS `_ - In both services while broadcasting, it is not guaranteed that a student always see the proctor. Usually the meeting service shows or pins the participant that is currently speaking automatically. - + + +Finished Exams +-------------- + +Since SEB Server version 1.4 there is a new section "Finished Exams" within the monitoring section to view finished and archived exams +like you do within the monitoring. You see all the SEB connections that has been connected to the exam when running and are able to view +particular SEB client connection details by either double-click on a SEB client connection entry in the list or by selection and using the View action +on the right action pane. + +In the "Finished Exams" list you can see all finished or archived exams and filter the list by Name, State and Type. + +.. image:: images/monitoring/finishedExams.png + :align: center + :target: https://raw.githubusercontent.com/SafeExamBrowser/seb-server/master/docs/images/monitoring/finishedExams.png + +To see a particular finished or archived exam you can just double-click in the list entry or use the View action on the right action pane. +In the exam view you see all SEB connections that has been connected to the exam during the exam run just like in the usual monitoring view +but with no update since the SEB connections are not active and the data is not changing anymore. You are able to filter the list by +User or Session Info, Connection Info or Status and are also be able to sort the list even for indicator columns. + +.. image:: images/monitoring/finishedExam.png + :align: center + :target: https://raw.githubusercontent.com/SafeExamBrowser/seb-server/master/docs/images/monitoring/finishedExam.png + +As in the usual monitoring view, you can show SEB client connection details by double-clicking on a list entry or by selecting a list entry +and use the View action on the right action pane. +In the detail view you see the same information for a particular SEB client connection as within the usual monitoring view. You can view +the SEB client logs of a SEB client connection here and analyze it after the exam was running. + +.. image:: images/monitoring/finishedClientConnection.png + :align: center + :target: https://raw.githubusercontent.com/SafeExamBrowser/seb-server/master/docs/images/monitoring/finishedClientConnection.png All SEB Client Logs diff --git a/docs/overview.rst b/docs/overview.rst index 65e2e282..3c95f7fc 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -160,6 +160,20 @@ that do not have a sort functionality yet. Most columns have a short tool-tip description that pops up while the mouse pointer stays over the column header for a moment. A column tool-tip usually also explains how to use the column-related filter. +**List Multi-Selection** + +Since SEB Server version 1.4, multi-selection for some lists with bulk-actions is possible. To select multiple rows in a table that allows multi-selection +just click on the row as usual. If you then click on another (still not selected) row, this row get selected too. You can do this even over several pages. +To deselect a selected row just click it again then it will be removed from the selection. + +.. image:: images/overview/list_multiselect.png + :align: center + :target: https://raw.githubusercontent.com/SafeExamBrowser/seb-server/master/docs/images/overview/list_multiselect.png + +.. note:: + Some actions on the right action pane are used only for single objects but also enabled on multi-selection. If you have multiple selections + and use a single object action like "View", "Edit" or "Copy" for exmaple, then the system will take the fist selected object/row to work with. + **Forms** Forms are used for domain entity specific data input or presentation, like HTML Forms usually do. Forms appear in three @@ -191,3 +205,20 @@ After correcting the missing or wrong input and saving the form again, the SEB S .. note:: If you navigate away from a form in edit mode, the GUI will inform you about possible data loss on this action and will prompt you to proceed or abort the action. + + +**Actions** + +Actions are usually placed on the right action pane of the application and belongs to the actual site or view. There are generally three types of actions: + +- Form Actions that directly belongs to the actual view or object and either save, manipulate or create a new object. +- List Action - Single Selection are actions on a list page that effects the selected list entry. +- List Action - Multi Selection are actions that refer to the current multi selection on a list and apply for every selected item. + +.. note:: + List action are disabled when nothing is selected from the list and get enabled as soon as one or more list items are selected. + Actions that are considdered single selection actions, and are used with a multi selection on the list will only affect the first selected item in the list. + +.. image:: images/overview/list_multiselect_actions.png + :align: center + :target: https://raw.githubusercontent.com/SafeExamBrowser/seb-server/master/docs/images/overview/list_multiselect_actions.png diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index b65140a6..3af68939 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -6,7 +6,29 @@ There shall be at least a problem description, an optional explanation if needed Please also have a look at `Open Issues `_ and/or `Ongoing Discussions `_ on the Git-Hub page. +-------------------------------- +- **Version** : 1.3.x + +- **Domain** : Exam Monitoring + +- **Problem** : SEB connections get lost and ping-times go up for already connected SEB clients + +- **Explanation** : This issue is due to a access token used by SEB client to authenticate on SEB Server that lasts not longer the one hour since SEB Server version 1.3 and since SEB client has no new access token request implements yet. + +- **Solution** : A workaround for SEB Server version 1.3.x is to make the access token expiry-date last long enough to minimize the possibility that the access token became invalid during a exam. We recommend to set it to 12 hours = 43200 seconds. Therefore please set the following SEB Server setup properties in the respective application-prod.properties configuration file of your SEB Server setup: + + sebserver.webservice.api.admin.accessTokenValiditySeconds=43200 + sebserver.webservice.api.exam.accessTokenValiditySeconds=43200 + +In SEB Server version 1.4 this is already set as default again and we are currently working on new SEB client versions that also +handle SEB Server communication token expiry by automatically requesting a new access token (with new lifetime) from SEB Server +when an old access token is not valid any longer. + +-------------------------------- + + +**Template** -------------------------------- - **Version** : 1.0.0 diff --git a/pom.xml b/pom.xml index 39138315..bb0e4b3b 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ jar - 1.3.3 + 1.4.0 ${sebserver-version} ${sebserver-version} UTF-8 @@ -125,7 +125,7 @@ - + + + com.github.spotbugs + spotbugs-maven-plugin + 4.5.3.0 + + Max + false + Low + true + findbugs-excludes.xml + + + + + com.github.spotbugs + spotbugs + 4.6.0 + + + org.jacoco jacoco-maven-plugin @@ -290,6 +310,15 @@ org.springframework.boot spring-boot-starter-validation + + + + org.springdoc + springdoc-openapi-ui + 1.5.10 + + + diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java b/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java index 9a35fbd7..b6116cdb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java @@ -8,7 +8,11 @@ package ch.ethz.seb.sebserver.gbl; +import java.text.Collator; +import java.util.Arrays; import java.util.Collection; +import java.util.List; +import java.util.Locale; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.RGBA; @@ -19,6 +23,7 @@ import org.springframework.core.ParameterizedTypeReference; import com.fasterxml.jackson.core.type.TypeReference; import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege; /** Global Constants used in SEB Server web-service as well as in web-gui component */ @@ -155,6 +160,23 @@ public final class Constants { public static final RGB BLACK_RGB = new RGB(0, 0, 0); public static final RGBA GREY_DISABLED = new RGBA(150, 150, 150, 50); + public static final Collator DEFAULT_ENGLISH_COLLATOR = Collator.getInstance(Locale.ENGLISH); + + public static final List ENTITY_TYPE_HIRARCHIE = Arrays.asList( + EntityType.INSTITUTION, + EntityType.USER, + EntityType.USER_ACTIVITY_LOG, + EntityType.CERTIFICATE, + EntityType.LMS_SETUP, + EntityType.SEB_CLIENT_CONFIGURATION, + EntityType.EXAM_TEMPLATE, + EntityType.EXAM, + EntityType.INDICATOR, + EntityType.EXAM_CONFIGURATION_MAP, + EntityType.CONFIGURATION_NODE, + EntityType.CLIENT_CONNECTION, + EntityType.CLIENT_EVENT); + public static final String IMPORTED_PASSWORD_MARKER = "_IMPORTED_PASSWORD"; public static final TypeReference> TYPE_REFERENCE_API_MESSAGE = diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java index 2c5c715f..87d85745 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java @@ -20,7 +20,7 @@ public final class API { public enum BatchActionType { EXAM_CONFIG_STATE_CHANGE(EntityType.CONFIGURATION_NODE), - EXAM_CONFIG_APPLY_TEMPLATE_VALUES(EntityType.CONFIGURATION_NODE); + EXAM_CONFIG_REST_TEMPLATE_SETTINGS(EntityType.CONFIGURATION_NODE); public final EntityType entityType; @@ -75,7 +75,7 @@ public final class API { public static final String LIST_PATH_SEGMENT = "/list"; public static final String ACTIVE_PATH_SEGMENT = "/active"; - + public static final String TOGGLE_ACTIVITY_PATH_SEGMENT = "/toggle-activity"; public static final String INACTIVE_PATH_SEGMENT = "/inactive"; public static final String DEPENDENCY_PATH_SEGMENT = "/dependency"; @@ -145,6 +145,7 @@ public final class API { public static final String EXAM_ADMINISTRATION_ENDPOINT = "/exam"; //public static final String EXAM_ADMINISTRATION_DOWNLOAD_CONFIG_PATH_SEGMENT = "/download-config"; + public static final String EXAM_ADMINISTRATION_ARCHIVE_PATH_SEGMENT = "/archive"; public static final String EXAM_ADMINISTRATION_CONSISTENCY_CHECK_PATH_SEGMENT = "/check-consistency"; public static final String EXAM_ADMINISTRATION_CONSISTENCY_CHECK_INCLUDE_RESTRICTION = "include-restriction"; public static final String EXAM_ADMINISTRATION_SEB_RESTRICTION_PATH_SEGMENT = "/seb-restriction"; @@ -156,6 +157,7 @@ public final class API { public static final String EXAM_INDICATOR_ENDPOINT = "/indicator"; public static final String SEB_CLIENT_CONFIG_ENDPOINT = "/client_configuration"; + public static final String SEB_CLIENT_CONFIG_CREDENTIALS_PATH_SEGMENT = "/credentials"; public static final String SEB_CLIENT_CONFIG_DOWNLOAD_PATH_SEGMENT = "/download"; public static final String CONFIGURATION_NODE_ENDPOINT = "/configuration-node"; @@ -167,6 +169,7 @@ public final class API { public static final String CONFIGURATION_UNDO_PATH_SEGMENT = "/undo"; public static final String CONFIGURATION_COPY_PATH_SEGMENT = "/copy"; public static final String CONFIGURATION_RESTORE_FROM_HISTORY_PATH_SEGMENT = "/restore"; + public static final String CONFIGURATION_RESET_TO_TEMPLATE_PATH_SEGMENT = "/reset-to-template"; public static final String CONFIGURATION_VALUE_ENDPOINT = "/configuration_value"; public static final String CONFIGURATION_TABLE_VALUE_PATH_SEGMENT = "/table"; public static final String CONFIGURATION_ATTRIBUTE_ENDPOINT = "/configuration_attribute"; @@ -193,6 +196,7 @@ public final class API { public static final String EXAM_MONITORING_NOTIFICATION_ENDPOINT = "/notification"; public static final String EXAM_MONITORING_DISABLE_CONNECTION_ENDPOINT = "/disable-connection"; public static final String EXAM_MONITORING_STATE_FILTER = "hidden-states"; + public static final String EXAM_MONITORING_FINISHED_ENDPOINT = "/finishedexams"; public static final String EXAM_MONITORING_SEB_CONNECTION_TOKEN_PATH_SEGMENT = "/{" + EXAM_API_SEB_CONNECTION_TOKEN + "}"; @@ -212,6 +216,7 @@ public final class API { public static final String EXAM_PROCTORING_ATTR_ALLOW_CHAT = "allow_chat"; public static final String SEB_CLIENT_CONNECTION_ENDPOINT = "/seb-client-connection"; + public static final String SEB_CLIENT_CONNECTION_DATA_ENDPOINT = "/data"; public static final String SEB_CLIENT_EVENT_ENDPOINT = "/seb-client-event"; public static final String SEB_CLIENT_EVENT_SEARCH_PATH_SEGMENT = "/search"; @@ -230,4 +235,6 @@ public final class API { public static final String EXAM_TEMPLATE_INDICATOR_PATH_SEGMENT = "/indicator"; public static final String EXAM_TEMPLATE_DEFAULT_PATH_SEGMENT = "/default"; + public static final String BATCH_ACTION_ENDPOINT = "/batch-action"; + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessage.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessage.java index e8393fa0..37da4881 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessage.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessage.java @@ -246,7 +246,7 @@ public class APIMessage implements Serializable { } public APIMessageException(final APIMessage apiMessage) { - super(); + super(apiMessage.systemMessage + " " + apiMessage.details); this.apiMessages = Arrays.asList(apiMessage); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessageError.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessageError.java index e6597fd5..7996c534 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessageError.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/APIMessageError.java @@ -18,4 +18,15 @@ public interface APIMessageError { * @return a List of APIMessage errors if error happened or empty list of not */ Collection getAPIMessages(); + /** Get the main APIMessage (first APIMessage in the list) or null if none available + * + * @return the main APIMessage or null if none available */ + default APIMessage getMainMessage() { + final Collection apiMessages = getAPIMessages(); + if (apiMessages != null && !apiMessages.isEmpty()) { + return apiMessages.iterator().next(); + } + return null; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/EntityType.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/EntityType.java index 8dd40055..a4a6c6bb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/EntityType.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/EntityType.java @@ -2,7 +2,7 @@ package ch.ethz.seb.sebserver.gbl.api; import javax.annotation.Generated; -@Generated(value="org.mybatis.generator.api.MyBatisGenerator",comments="ch.ethz.seb.sebserver.gen.DomainModelNameReferencePlugin",date="2022-01-18T17:36:21.033+01:00") +@Generated(value="org.mybatis.generator.api.MyBatisGenerator",comments="ch.ethz.seb.sebserver.gen.DomainModelNameReferencePlugin",date="2022-05-16T11:24:18.256+02:00") public enum EntityType { CONFIGURATION_ATTRIBUTE, CONFIGURATION_VALUE, diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/POSTMapper.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/POSTMapper.java index 3fccdbab..4cc325b2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/POSTMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/POSTMapper.java @@ -14,7 +14,10 @@ import java.util.Base64; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; import org.apache.commons.lang3.BooleanUtils; @@ -226,6 +229,14 @@ public class POSTMapper { return Utils.toDateTime(value); } + public Map getSubMap(final Set actionAttributes) { + return this.params + .keySet() + .stream() + .filter(actionAttributes::contains) + .collect(Collectors.toMap(Function.identity(), k -> this.params.getFirst(k))); + } + public List getThresholds() { final List thresholdStrings = this.params.get(Domain.THRESHOLD.REFERENCE_NAME); if (thresholdStrings == null || thresholdStrings.isEmpty()) { @@ -237,14 +248,20 @@ public class POSTMapper { .map(ts -> { try { final String[] split = StringUtils.split(ts, Constants.EMBEDDED_LIST_SEPARATOR); - return new Threshold(Double.parseDouble( - split[0]), - (split.length > 1) ? split[1] : null, - (split.length > 2) ? split[2] : null); + Double val = null; + try { + val = Double.parseDouble(split[0]); + } catch (final Exception e) { + } + return new Threshold( + val, + (split.length > 1) ? Utils.parseColorString(Utils.parseRGB(split[1])) : null, + (split.length > 2 && "null".equals(split[2])) ? split[2] : null); } catch (final Exception e) { return null; } }) + .filter(Objects::nonNull) .collect(Collectors.toList()); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/authorization/PrivilegeType.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/authorization/PrivilegeType.java index e4bc9c20..2e8abfde 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/authorization/PrivilegeType.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/authorization/PrivilegeType.java @@ -29,7 +29,7 @@ public enum PrivilegeType { * and so on. * * @param type the PrivilegeType - * @return true if given PrivilegeType is implicit form this PrivilegeType */ + * @return true if given PrivilegeType is implicit from this PrivilegeType */ public boolean hasImplicit(final PrivilegeType type) { if (type == null) { return false; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java b/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java index 17f9f816..a6a1d46e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java @@ -36,7 +36,7 @@ public class AsyncService { * * @param maxFailingAttempts maximal number of attempts the CircuitBreaker allows before going onto open state. * @param maxBlockingTime maximal time since call CircuitBreaker waits for a response before going onto open state. - * @param timeToRecover the time the CircuitBreaker takes to recover form open state. + * @param timeToRecover the time the CircuitBreaker takes to recover from open state. * @param the type of the CircuitBreaker * @return a CircuitBreaker of specified type */ public CircuitBreaker createCircuitBreaker( @@ -57,7 +57,7 @@ public class AsyncService { * @param blockingSupplier the blocking result supplier that the MemoizingCircuitBreaker must call * @param maxFailingAttempts maximal number of attempts the CircuitBreaker allows before going onto open state. * @param maxBlockingTime maximal time since call CircuitBreaker waits for a response before going onto open state. - * @param timeToRecover the time the CircuitBreaker takes to recover form open state. + * @param timeToRecover the time the CircuitBreaker takes to recover from open state. * @param momoized whether the memoizing functionality is on or off * @param maxMemoizingTime the maximal time memorized data is valid * @param the type of the CircuitBreaker diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreaker.java b/src/main/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreaker.java index 2493bb23..61d85d65 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreaker.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreaker.java @@ -8,8 +8,10 @@ package ch.ethz.seb.sebserver.gbl.async; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; @@ -50,13 +52,11 @@ public final class CircuitBreaker { private static final Logger log = LoggerFactory.getLogger(CircuitBreaker.class); + public static final String OPEN_CIRCUIT_BREAKER_EXCEPTION = "Open CircuitBreaker"; public static final int DEFAULT_MAX_FAILING_ATTEMPTS = 5; public static final long DEFAULT_MAX_BLOCKING_TIME = Constants.MINUTE_IN_MILLIS; public static final long DEFAULT_TIME_TO_RECOVER = Constants.MINUTE_IN_MILLIS * 10; - public static final RuntimeException OPEN_STATE_EXCEPTION = - new RuntimeException("Open CircuitBreaker"); - public enum State { CLOSED, HALF_OPEN, @@ -87,7 +87,7 @@ public final class CircuitBreaker { /** Create new CircuitBreakerSupplier. * * @param asyncRunner the AsyncRunner used to create asynchronous calls on the given supplier function - * @param maxFailingAttempts the number of maximal failing attempts before go form CLOSE into HALF_OPEN state + * @param maxFailingAttempts the number of maximal failing attempts before go from CLOSE into HALF_OPEN state * @param maxBlockingTime the maximal time that an call attempt can block until an error is responded * @param timeToRecover the time the circuit breaker needs to cool-down on OPEN-STATE before going back to HALF_OPEN * state */ @@ -169,10 +169,10 @@ public final class CircuitBreaker { final long currentBlockingTime = Utils.getMillisecondsNow() - startTime; final int failing = this.failingCount.incrementAndGet(); - if (failing > this.maxFailingAttempts || currentBlockingTime > this.maxBlockingTime) { + if (failing >= this.maxFailingAttempts || currentBlockingTime > this.maxBlockingTime) { // brake thought to HALF_OPEN state and return error if (log.isDebugEnabled()) { - log.debug("Changing state from Open to Half Open and return cached value"); + log.debug("Changing state from Open to Half Open"); } this.state = State.HALF_OPEN; @@ -203,7 +203,7 @@ public final class CircuitBreaker { if (result.hasError()) { // on fail go to OPEN state if (log.isDebugEnabled()) { - log.debug("Changing state from Half Open to Open and return cached value"); + log.debug("Changing state from Half Open to Open"); } this.lastOpenTime = Utils.getMillisecondsNow(); @@ -214,7 +214,7 @@ public final class CircuitBreaker { } else { // on success go to CLOSED state if (log.isDebugEnabled()) { - log.debug("Changing state from Half Open to Closed and return value"); + log.debug("Changing state from Half Open to Closed"); } this.state = State.CLOSED; @@ -243,14 +243,26 @@ public final class CircuitBreaker { return protectedRun(supplier); } - return Result.ofError(OPEN_STATE_EXCEPTION); + return Result.ofError(new RuntimeException(OPEN_CIRCUIT_BREAKER_EXCEPTION)); } private Result attempt(final Supplier supplier) { final Future future = this.asyncRunner.runAsync(supplier); + try { return Result.of(future.get(this.maxBlockingTime, TimeUnit.MILLISECONDS)); - } catch (final Exception e) { + } catch (final InterruptedException e) { + if (log.isDebugEnabled()) { + log.debug("Attempt interruption: {}, {}", e.getMessage(), this.state); + } + return Result.ofError(e); + } catch (final ExecutionException e) { + future.cancel(false); + if (log.isWarnEnabled()) { + log.warn("Attempt error: {}, {}", e.getMessage(), this.state); + } + return Result.ofError(e); + } catch (final TimeoutException e) { future.cancel(false); log.warn("Max blocking timeout exceeded: {}, {}", this.maxBlockingTime, this.state); return Result.ofError(e); diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreaker.java b/src/main/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreaker.java index 5a236a5d..f32f3a62 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreaker.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreaker.java @@ -94,7 +94,7 @@ public final class MemoizingCircuitBreaker implements Supplier> { * * @param asyncRunner the AsyncRunner used to create asynchronous calls on the given supplier function * @param supplier The Supplier function that can fail or block for a long time - * @param maxFailingAttempts the number of maximal failing attempts before go form CLOSE into HALF_OPEN state + * @param maxFailingAttempts the number of maximal failing attempts before go from CLOSE into HALF_OPEN state * @param maxBlockingTime the maximal time that an call attempt can block until an error is responded * @param timeToRecover the time the circuit breaker needs to cool-down on OPEN-STATE before going back to HALF_OPEN * state diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/client/ClientCredentialService.java b/src/main/java/ch/ethz/seb/sebserver/gbl/client/ClientCredentialService.java index d28e6664..a61e8b39 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/client/ClientCredentialService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/client/ClientCredentialService.java @@ -55,13 +55,13 @@ public interface ClientCredentialService { return encryptClientCredentials(clientIdPlaintext, secretPlaintext, null); } - /** Use this to get a decrypted plain text secret form given ClientCredentials + /** Use this to get a decrypted plain text secret from given ClientCredentials * * @param credentials ClientCredentials containing the secret to decrypt * @return decrypted plain text secret */ Result getPlainClientSecret(ClientCredentials credentials); - /** Use this to get a decrypted plain text accessToken form given ClientCredentials + /** Use this to get a decrypted plain text accessToken from given ClientCredentials * * @param credentials ClientCredentials containing the accessToken to decrypt * @return decrypted plain text accessToken */ diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/client/ClientCredentials.java b/src/main/java/ch/ethz/seb/sebserver/gbl/client/ClientCredentials.java index 52d5304d..f98378cd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/client/ClientCredentials.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/client/ClientCredentials.java @@ -8,20 +8,34 @@ package ch.ethz.seb.sebserver.gbl.client; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + /** Defines a simple data bean holding (encrypted) client credentials */ +@JsonIgnoreProperties(ignoreUnknown = true) public final class ClientCredentials { + public static final String ATTR_CLIENT_ID = "clientId"; + public static final String ATTR_SECRET = "secret"; + public static final String ATTR_ACCESS_TOKEN = "accessToken"; + /** The client id or client name parameter */ + @JsonProperty(ATTR_CLIENT_ID) public final CharSequence clientId; /** The client secret parameter */ + @JsonProperty(ATTR_SECRET) public final CharSequence secret; /** An client access token if supported */ + @JsonProperty(ATTR_ACCESS_TOKEN) public final CharSequence accessToken; + @JsonCreator public ClientCredentials( - final CharSequence clientId, - final CharSequence secret, - final CharSequence accessToken) { + @JsonProperty(ATTR_CLIENT_ID) final CharSequence clientId, + @JsonProperty(ATTR_SECRET) final CharSequence secret, + @JsonProperty(ATTR_ACCESS_TOKEN) final CharSequence accessToken) { this.clientId = clientId; this.secret = secret; @@ -35,26 +49,44 @@ public final class ClientCredentials { this(clientId, secret, null); } + public CharSequence getClientId() { + return this.clientId; + } + + public CharSequence getSecret() { + return this.secret; + } + + public CharSequence getAccessToken() { + return this.accessToken; + } + + @JsonIgnore public boolean hasClientId() { return this.clientId != null && this.clientId.length() > 0; } + @JsonIgnore public boolean hasSecret() { return this.secret != null && this.secret.length() > 0; } + @JsonIgnore public boolean hasAccessToken() { return this.accessToken != null && this.accessToken.length() > 0; } + @JsonIgnore public String clientIdAsString() { return hasClientId() ? this.clientId.toString() : null; } + @JsonIgnore public String secretAsString() { return hasSecret() ? this.secret.toString() : null; } + @JsonIgnore public String accessTokenAsString() { return hasAccessToken() ? this.accessToken.toString() : null; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/BatchAction.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/BatchAction.java index 9aa3d36e..cae4ab76 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/BatchAction.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/BatchAction.java @@ -8,18 +8,36 @@ package ch.ethz.seb.sebserver.gbl.model; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import javax.validation.constraints.NotNull; +import org.apache.commons.lang3.StringUtils; + +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.model.Domain.BATCH_ACTION; import ch.ethz.seb.sebserver.gbl.util.Utils; -public class BatchAction implements Entity { +public class BatchAction implements GrantEntity { + + public static final String ATTR_FAILURES = "failures"; + public static final String FINISHED_FLAG = "_FINISHED"; + public static final String ACTION_ATTRIBUT_TARGET_STATE = "batchActionTargetState"; + + private static final Set ACTION_ATTRIBUTES = new HashSet<>(Arrays.asList( + ACTION_ATTRIBUT_TARGET_STATE)); @JsonProperty(BATCH_ACTION.ATTR_ID) public final Long id; @@ -28,10 +46,17 @@ public class BatchAction implements Entity { @JsonProperty(BATCH_ACTION.ATTR_INSTITUTION_ID) public final Long institutionId; + @NotNull + @JsonProperty(BATCH_ACTION.ATTR_OWNER) + public final String ownerId; + @NotNull @JsonProperty(BATCH_ACTION.ATTR_ACTION_TYPE) public final BatchActionType actionType; + @JsonProperty(BATCH_ACTION.ATTR_ATTRIBUTES) + public final Map attributes; + @NotNull @JsonProperty(BATCH_ACTION.ATTR_SOURCE_IDS) public final Collection sourceIds; @@ -45,23 +70,49 @@ public class BatchAction implements Entity { @JsonProperty(BATCH_ACTION.ATTR_PROCESSOR_ID) public final String processorId; + @JsonProperty(ATTR_FAILURES) + public final Map failures; + public BatchAction( @JsonProperty(BATCH_ACTION.ATTR_ID) final Long id, @JsonProperty(BATCH_ACTION.ATTR_INSTITUTION_ID) final Long institutionId, + @JsonProperty(BATCH_ACTION.ATTR_OWNER) final String ownerId, @JsonProperty(BATCH_ACTION.ATTR_ACTION_TYPE) final BatchActionType actionType, + @JsonProperty(BATCH_ACTION.ATTR_ATTRIBUTES) final Map attributes, @JsonProperty(BATCH_ACTION.ATTR_SOURCE_IDS) final Collection sourceIds, @JsonProperty(BATCH_ACTION.ATTR_SUCCESSFUL) final Collection successful, @JsonProperty(BATCH_ACTION.ATTR_LAST_UPDATE) final Long lastUpdate, - @JsonProperty(BATCH_ACTION.ATTR_PROCESSOR_ID) final String processorId) { + @JsonProperty(BATCH_ACTION.ATTR_PROCESSOR_ID) final String processorId, + @JsonProperty(ATTR_FAILURES) final Map failures) { super(); this.id = id; this.institutionId = institutionId; + this.ownerId = ownerId; this.actionType = actionType; + this.attributes = Utils.immutableMapOf(attributes); this.sourceIds = Utils.immutableCollectionOf(sourceIds); this.successful = Utils.immutableCollectionOf(successful); this.lastUpdate = lastUpdate; this.processorId = processorId; + this.failures = Utils.immutableMapOf(failures); + } + + public BatchAction(final String modelId, final String ownerId, final POSTMapper postMap) { + + super(); + this.id = (modelId != null) ? Long.parseLong(modelId) : null; + this.institutionId = postMap.getLong(BATCH_ACTION.ATTR_INSTITUTION_ID); + this.ownerId = ownerId; + this.actionType = postMap.getEnum(BATCH_ACTION.ATTR_ACTION_TYPE, BatchActionType.class); + this.attributes = postMap.getSubMap(ACTION_ATTRIBUTES); + this.sourceIds = Utils.immutableListOf(StringUtils.split( + postMap.getString(BATCH_ACTION.ATTR_SOURCE_IDS), + Constants.LIST_SEPARATOR)); + this.successful = Collections.emptyList(); + this.lastUpdate = null; + this.processorId = null; + this.failures = Collections.emptyMap(); } @Override @@ -85,14 +136,24 @@ public class BatchAction implements Entity { return this.id; } + @Override public Long getInstitutionId() { return this.institutionId; } + @Override + public String getOwnerId() { + return this.ownerId; + } + public BatchActionType getActionType() { return this.actionType; } + public Map getAttributes() { + return this.attributes; + } + public Collection getSourceIds() { return this.sourceIds; } @@ -109,6 +170,20 @@ public class BatchAction implements Entity { return this.processorId; } + public Map getFailures() { + return this.failures; + } + + @JsonIgnore + public int getProgress() { + return 100 / this.sourceIds.size() * (this.successful.size() + this.failures.size()); + } + + @JsonIgnore + public boolean isFinished() { + return this.processorId != null && this.processorId.contains(FINISHED_FLAG); + } + @Override public int hashCode() { final int prime = 31; @@ -143,6 +218,8 @@ public class BatchAction implements Entity { builder.append(this.institutionId); builder.append(", actionType="); builder.append(this.actionType); + builder.append(", attributes="); + builder.append(this.attributes); builder.append(", sourceIds="); builder.append(this.sourceIds); builder.append(", successful="); diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/Domain.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/Domain.java index ae19e193..c67ad7ea 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/Domain.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/Domain.java @@ -5,7 +5,7 @@ import javax.annotation.Generated; /** Defines the global names of the domain model and domain model fields. * This shall be used as a static overall domain model names reference within SEB Server Web-Service as well as within the integrated GUI * This file is generated by the org.eth.demo.sebserver.gen.DomainModelNameReferencePlugin and must not be edited manually.**/ -@Generated(value="org.mybatis.generator.api.MyBatisGenerator",comments="ch.ethz.seb.sebserver.gen.DomainModelNameReferencePlugin",date="2022-01-18T17:36:20.975+01:00") +@Generated(value="org.mybatis.generator.api.MyBatisGenerator",comments="ch.ethz.seb.sebserver.gen.DomainModelNameReferencePlugin",date="2022-05-16T11:24:18.186+02:00") public interface Domain { interface CONFIGURATION_ATTRIBUTE { @@ -79,6 +79,8 @@ public interface Domain { String ATTR_DESCRIPTION = "description"; String ATTR_TYPE = "type"; String ATTR_STATUS = "status"; + String ATTR_LAST_UPDATE_TIME = "lastUpdateTime"; + String ATTR_LAST_UPDATE_USER = "lastUpdateUser"; } interface EXAM_CONFIGURATION_MAP { @@ -111,6 +113,10 @@ public interface Domain { String ATTR_ACTIVE = "active"; String ATTR_EXAM_TEMPLATE_ID = "examTemplateId"; String ATTR_LAST_MODIFIED = "lastModified"; + String ATTR_QUIZ_NAME = "quizName"; + String ATTR_QUIZ_START_TIME = "quizStartTime"; + String ATTR_QUIZ_END_TIME = "quizEndTime"; + String ATTR_LMS_AVAILABLE = "lmsAvailable"; } interface CLIENT_CONNECTION { @@ -217,6 +223,8 @@ public interface Domain { String ATTR_CLIENT_SECRET = "clientSecret"; String ATTR_ENCRYPT_SECRET = "encryptSecret"; String ATTR_ACTIVE = "active"; + String ATTR_LAST_UPDATE_TIME = "lastUpdateTime"; + String ATTR_LAST_UPDATE_USER = "lastUpdateUser"; } interface LMS_SETUP { @@ -323,7 +331,9 @@ public interface Domain { String REFERENCE_NAME = "batchActions"; String ATTR_ID = "id"; String ATTR_INSTITUTION_ID = "institutionId"; + String ATTR_OWNER = "owner"; String ATTR_ACTION_TYPE = "actionType"; + String ATTR_ATTRIBUTES = "attributes"; String ATTR_SOURCE_IDS = "sourceIds"; String ATTR_SUCCESSFUL = "successful"; String ATTR_LAST_UPDATE = "lastUpdate"; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/Entity.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/Entity.java index ada4b1ae..451913f3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/Entity.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/Entity.java @@ -48,7 +48,7 @@ public interface Entity extends ModelIdAware { /** Creates an EntityName instance from this Entity instance. * - * @return EntityName instance created form given Entity */ + * @return EntityName instance created from given Entity */ default EntityName toName() { return new EntityName( this.getModelId(), diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityDependency.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityDependency.java index 1103a9d7..90170c50 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityDependency.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityDependency.java @@ -11,8 +11,10 @@ package ch.ethz.seb.sebserver.gbl.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import ch.ethz.seb.sebserver.gbl.Constants; + @JsonIgnoreProperties(ignoreUnknown = true) -public class EntityDependency implements Comparable { +public class EntityDependency implements Comparable, ModelIdAware { public static final String ATTR_PARENT = "parent"; public static final String ATTR_SELF = "self"; @@ -48,6 +50,11 @@ public class EntityDependency implements Comparable { return this.self; } + @Override + public String getModelId() { + return this.self.modelId; + } + public String getName() { return this.name; } @@ -108,9 +115,16 @@ public class EntityDependency implements Comparable { return -1; } - final int compareTo = this.self.entityType.name().compareTo(other.self.entityType.name()); + final int compareTo = Integer.compare( + Constants.ENTITY_TYPE_HIRARCHIE.indexOf(this.self.entityType), + Constants.ENTITY_TYPE_HIRARCHIE.indexOf(other.self.entityType)); + //this.self.entityType.name().compareTo(other.self.entityType.name()); if (compareTo == 0) { - return this.self.modelId.compareTo(other.self.modelId); + if (this.name != null) { + return this.name.compareTo(other.name); + } else { + return this.self.modelId.compareTo(other.self.modelId); + } } else { return compareTo; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java index cb3c0fef..bb0622b1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java @@ -16,6 +16,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.EntityType; /** An EntityKey uniquely identifies a domain entity within the SEB Server's domain model. @@ -128,7 +129,11 @@ public class EntityKey implements ModelIdAware, Serializable, Comparable additionalAttributes; + public final Map additionalAttributes; @JsonCreator public Exam( @@ -146,11 +144,10 @@ public final class Exam implements GrantEntity { @JsonProperty(EXAM.ATTR_INSTITUTION_ID) final Long institutionId, @JsonProperty(EXAM.ATTR_LMS_SETUP_ID) final Long lmsSetupId, @JsonProperty(EXAM.ATTR_EXTERNAL_ID) final String externalId, - @JsonProperty(QuizData.QUIZ_ATTR_NAME) final String name, - @JsonProperty(QuizData.QUIZ_ATTR_DESCRIPTION) final String description, - @JsonProperty(QuizData.QUIZ_ATTR_START_TIME) final DateTime startTime, - @JsonProperty(QuizData.QUIZ_ATTR_END_TIME) final DateTime endTime, - @JsonProperty(QuizData.QUIZ_ATTR_START_URL) final String startURL, + @JsonProperty(EXAM.ATTR_LMS_AVAILABLE) final Boolean lmsAvailable, + @JsonProperty(EXAM.ATTR_QUIZ_NAME) final String name, + @JsonProperty(EXAM.ATTR_QUIZ_START_TIME) final DateTime startTime, + @JsonProperty(EXAM.ATTR_QUIZ_END_TIME) final DateTime endTime, @JsonProperty(EXAM.ATTR_TYPE) final ExamType type, @JsonProperty(EXAM.ATTR_OWNER) final String owner, @JsonProperty(EXAM.ATTR_SUPPORTER) final Collection supporter, @@ -167,11 +164,10 @@ public final class Exam implements GrantEntity { this.institutionId = institutionId; this.lmsSetupId = lmsSetupId; this.externalId = externalId; + this.lmsAvailable = lmsAvailable; this.name = name; - this.description = description; this.startTime = startTime; this.endTime = endTime; - this.startURL = startURL; this.type = type; this.owner = owner; this.status = (status != null) ? status : getStatusFromDate(startTime, endTime); @@ -191,15 +187,18 @@ public final class Exam implements GrantEntity { public Exam(final String modelId, final QuizData quizData, final POSTMapper mapper) { + final Map additionalAttributes = new HashMap<>(quizData.getAdditionalAttributes()); + additionalAttributes.put(QuizData.QUIZ_ATTR_DESCRIPTION, quizData.description); + additionalAttributes.put(QuizData.QUIZ_ATTR_START_URL, quizData.startURL); + this.id = (modelId != null) ? Long.parseLong(modelId) : null; this.institutionId = quizData.institutionId; this.lmsSetupId = quizData.lmsSetupId; this.externalId = quizData.id; + this.lmsAvailable = true; this.name = quizData.name; - this.description = quizData.description; this.startTime = quizData.startTime; this.endTime = quizData.endTime; - this.startURL = quizData.startURL; this.type = mapper.getEnum(EXAM.ATTR_TYPE, ExamType.class, ExamType.UNDEFINED); this.owner = mapper.getString(EXAM.ATTR_OWNER); this.status = mapper.getEnum( @@ -213,7 +212,8 @@ public final class Exam implements GrantEntity { this.lastUpdate = null; this.examTemplateId = mapper.getLong(EXAM.ATTR_EXAM_TEMPLATE_ID); this.lastModified = null; - this.additionalAttributes = null; + this.additionalAttributes = Utils.immutableMapOf(additionalAttributes); + } public Exam(final QuizData quizData) { @@ -225,11 +225,10 @@ public final class Exam implements GrantEntity { this.institutionId = null; this.lmsSetupId = null; this.externalId = null; + this.lmsAvailable = true; this.name = null; - this.description = null; this.startTime = null; this.endTime = null; - this.startURL = null; this.type = null; this.owner = null; this.status = (status != null) ? status : getStatusFromDate(this.startTime, this.endTime); @@ -293,6 +292,15 @@ public final class Exam implements GrantEntity { return this.lmsSetupId; } + public Boolean getLmsAvailable() { + return this.lmsAvailable; + } + + @JsonIgnore + public boolean isLmsAvailable() { + return BooleanUtils.isTrue(this.lmsAvailable); + } + public String getExternalId() { return this.externalId; } @@ -311,7 +319,7 @@ public final class Exam implements GrantEntity { } public String getDescription() { - return this.description; + return this.getAdditionalAttribute(QuizData.QUIZ_ATTR_DESCRIPTION); } public DateTime getStartTime() { @@ -323,7 +331,7 @@ public final class Exam implements GrantEntity { } public String getStartURL() { - return this.startURL; + return this.getAdditionalAttribute(QuizData.QUIZ_ATTR_START_URL); } public ExamStatus getStatus() { @@ -372,13 +380,13 @@ public final class Exam implements GrantEntity { builder.append(", name="); builder.append(this.name); builder.append(", description="); - builder.append(this.description); + builder.append(this.getDescription()); builder.append(", startTime="); builder.append(this.startTime); builder.append(", endTime="); builder.append(this.endTime); builder.append(", startURL="); - builder.append(this.startURL); + builder.append(this.getStartURL()); builder.append(", type="); builder.append(this.type); builder.append(", owner="); diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ExamConfigurationMap.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ExamConfigurationMap.java index c91154b4..b870871c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ExamConfigurationMap.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ExamConfigurationMap.java @@ -26,12 +26,15 @@ import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM; import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM_CONFIGURATION_MAP; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.GrantEntity; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus; @JsonIgnoreProperties(ignoreUnknown = true) public final class ExamConfigurationMap implements GrantEntity { + private static final String ARR_EXAM_STATUS = "examStatus"; + public static final String ATTR_CONFIRM_ENCRYPT_SECRET = "confirm_encrypt_secret"; public static final String FILTER_ATTR_EXAM_ID = "examId"; @@ -60,6 +63,9 @@ public final class ExamConfigurationMap implements GrantEntity { @JsonProperty(EXAM.ATTR_TYPE) public final ExamType examType; + @JsonProperty(ARR_EXAM_STATUS) + public final ExamStatus examStatus; + @NotNull(message = "examConfigurationMap:configurationNodeId:notNull") @JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_CONFIGURATION_NODE_ID) public final Long configurationNodeId; @@ -91,6 +97,7 @@ public final class ExamConfigurationMap implements GrantEntity { @JsonProperty(QuizData.QUIZ_ATTR_DESCRIPTION) final String examDescription, @JsonProperty(QuizData.QUIZ_ATTR_START_TIME) final DateTime examStartTime, @JsonProperty(EXAM.ATTR_TYPE) final ExamType examType, + @JsonProperty(ARR_EXAM_STATUS) final ExamStatus examStatus, @JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_CONFIGURATION_NODE_ID) final Long configurationNodeId, @JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_USER_NAMES) final String userNames, @JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_ENCRYPT_SECRET) final CharSequence encryptSecret, @@ -106,6 +113,7 @@ public final class ExamConfigurationMap implements GrantEntity { this.examDescription = examDescription; this.examStartTime = examStartTime; this.examType = examType; + this.examStatus = examStatus; this.configurationNodeId = configurationNodeId; this.userNames = userNames; this.encryptSecret = encryptSecret; @@ -125,6 +133,7 @@ public final class ExamConfigurationMap implements GrantEntity { this.examDescription = postParams.getString(QuizData.QUIZ_ATTR_DESCRIPTION); this.examStartTime = postParams.getDateTime(QuizData.QUIZ_ATTR_START_TIME); this.examType = postParams.getEnum(EXAM.ATTR_TYPE, ExamType.class); + this.examStatus = postParams.getEnum(ARR_EXAM_STATUS, ExamStatus.class); this.configurationNodeId = postParams.getLong(Domain.EXAM_CONFIGURATION_MAP.ATTR_CONFIGURATION_NODE_ID); this.userNames = postParams.getString(Domain.EXAM_CONFIGURATION_MAP.ATTR_USER_NAMES); @@ -149,6 +158,7 @@ public final class ExamConfigurationMap implements GrantEntity { this.examDescription = null; this.examStartTime = null; this.examType = null; + this.examStatus = null; this.configurationNodeId = configurationNodeId; this.userNames = userNames; this.encryptSecret = null; @@ -205,6 +215,10 @@ public final class ExamConfigurationMap implements GrantEntity { return this.examType; } + public ExamStatus getExamStatus() { + return this.examStatus; + } + public Long getConfigurationNodeId() { return this.configurationNodeId; } @@ -250,6 +264,7 @@ public final class ExamConfigurationMap implements GrantEntity { this.examDescription, this.examStartTime, this.examType, + this.examStatus, this.configurationNodeId, this.userNames, Constants.EMPTY_NOTE, @@ -296,7 +311,8 @@ public final class ExamConfigurationMap implements GrantEntity { public static ExamConfigurationMap createNew(final Exam exam) { return new ExamConfigurationMap( - null, exam.institutionId, exam.id, exam.name, exam.description, exam.startTime, exam.type, + null, exam.institutionId, exam.id, exam.name, exam.getDescription(), exam.startTime, exam.type, + exam.status, null, null, null, null, null, null, null); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Indicator.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Indicator.java index 2f15b2f2..21d119de 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Indicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Indicator.java @@ -240,6 +240,7 @@ public final class Indicator implements Entity { } } + @JsonIgnoreProperties(ignoreUnknown = true) public static final class Threshold implements Comparable { @JsonProperty(THRESHOLD.ATTR_VALUE) diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java index 7603eabc..c071df70 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java @@ -56,7 +56,7 @@ public final class LmsSetup implements GrantEntity, Activatable { public enum LmsType { /** Mockup LMS type used to create test setups */ MOCKUP(Features.COURSE_API), - /** The Open edX LMS binding features both APIs, course access as well as SEB restrcition */ + /** The Open edX LMS binding features both APIs, course access as well as SEB restriction */ OPEN_EDX(Features.COURSE_API, Features.SEB_RESTRICTION), /** The Moodle binding features only the course access API so far */ MOODLE(Features.COURSE_API /* , Features.SEB_RESTRICTION */), diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetupTestResult.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetupTestResult.java index 6cdc41e5..fbef5775 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetupTestResult.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetupTestResult.java @@ -28,6 +28,7 @@ public final class LmsSetupTestResult { public static final String ATTR_MISSING_ATTRIBUTE = "missingLMSSetupAttribute"; public enum ErrorType { + API_NOT_SUPPORTED, MISSING_ATTRIBUTE, TOKEN_REQUEST, QUIZ_ACCESS_API_REQUEST, @@ -109,7 +110,12 @@ public final class LmsSetupTestResult { return new LmsSetupTestResult(lmsType); } - public static LmsSetupTestResult ofMissingAttributes(final LmsSetup.LmsType lmsType, + public static LmsSetupTestResult ofAPINotSupported(final LmsSetup.LmsType lmsType) { + return new LmsSetupTestResult(lmsType, new Error(ErrorType.TOKEN_REQUEST, "Not Supported")); + } + + public static LmsSetupTestResult ofMissingAttributes( + final LmsSetup.LmsType lmsType, final Collection attrs) { return new LmsSetupTestResult(lmsType, new Error(ErrorType.MISSING_ATTRIBUTE, "missing attribute(s)"), attrs); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ConfigurationNode.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ConfigurationNode.java index 26edd117..707d0abb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ConfigurationNode.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ConfigurationNode.java @@ -11,13 +11,14 @@ package ch.ethz.seb.sebserver.gbl.model.sebconfig; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; +import org.joda.time.DateTime; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; -import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain.CONFIGURATION_NODE; import ch.ethz.seb.sebserver.gbl.model.GrantEntity; @@ -72,6 +73,12 @@ public final class ConfigurationNode implements GrantEntity { @JsonProperty(CONFIGURATION_NODE.ATTR_STATUS) public final ConfigurationStatus status; + @JsonProperty(CONFIGURATION_NODE.ATTR_LAST_UPDATE_TIME) + public final DateTime lastUpdateTime; + + @JsonProperty(CONFIGURATION_NODE.ATTR_LAST_UPDATE_USER) + public final String lastUpdateUser; + @JsonCreator public ConfigurationNode( @JsonProperty(CONFIGURATION_NODE.ATTR_ID) final Long id, @@ -81,7 +88,9 @@ public final class ConfigurationNode implements GrantEntity { @JsonProperty(CONFIGURATION_NODE.ATTR_DESCRIPTION) final String description, @JsonProperty(CONFIGURATION_NODE.ATTR_TYPE) final ConfigurationType type, @JsonProperty(CONFIGURATION_NODE.ATTR_OWNER) final String owner, - @JsonProperty(CONFIGURATION_NODE.ATTR_STATUS) final ConfigurationStatus status) { + @JsonProperty(CONFIGURATION_NODE.ATTR_STATUS) final ConfigurationStatus status, + @JsonProperty(CONFIGURATION_NODE.ATTR_LAST_UPDATE_TIME) final DateTime lastUpdateTime, + @JsonProperty(CONFIGURATION_NODE.ATTR_LAST_UPDATE_USER) final String lastUpdateUser) { this.id = id; this.institutionId = institutionId; @@ -91,24 +100,29 @@ public final class ConfigurationNode implements GrantEntity { this.type = (type != null) ? type : ConfigurationType.EXAM_CONFIG; this.owner = owner; this.status = status; + this.lastUpdateTime = lastUpdateTime; + this.lastUpdateUser = lastUpdateUser; } public ConfigurationNode(final Long institutionId, final POSTMapper postParams) { this.id = null; this.institutionId = institutionId; - final Long tplId = postParams.getLong(Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID); + final Long tplId = postParams.getLong(CONFIGURATION_NODE.ATTR_TEMPLATE_ID); this.templateId = (tplId != null) ? tplId : DEFAULT_TEMPLATE_ID; - this.name = postParams.getString(Domain.CONFIGURATION_NODE.ATTR_NAME); - this.description = postParams.getString(Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION); + this.name = postParams.getString(CONFIGURATION_NODE.ATTR_NAME); + this.description = postParams.getString(CONFIGURATION_NODE.ATTR_DESCRIPTION); this.type = postParams.getEnum( - Domain.CONFIGURATION_NODE.ATTR_TYPE, + CONFIGURATION_NODE.ATTR_TYPE, ConfigurationType.class, ConfigurationType.EXAM_CONFIG); - this.owner = postParams.getString(Domain.CONFIGURATION_NODE.ATTR_OWNER); + this.owner = postParams.getString(CONFIGURATION_NODE.ATTR_OWNER); this.status = postParams.getEnum( - Domain.CONFIGURATION_NODE.ATTR_STATUS, + CONFIGURATION_NODE.ATTR_STATUS, ConfigurationStatus.class, ConfigurationStatus.CONSTRUCTION); + this.lastUpdateTime = postParams.getDateTime(CONFIGURATION_NODE.ATTR_LAST_UPDATE_TIME); + this.lastUpdateUser = postParams.getString(CONFIGURATION_NODE.ATTR_LAST_UPDATE_USER); + } @Override @@ -158,6 +172,14 @@ public final class ConfigurationNode implements GrantEntity { return this.status; } + public DateTime getLastUpdateTime() { + return this.lastUpdateTime; + } + + public String getLastUpdateUser() { + return this.lastUpdateUser; + } + @Override public String toString() { final StringBuilder builder = new StringBuilder(); @@ -173,10 +195,12 @@ public final class ConfigurationNode implements GrantEntity { builder.append(this.description); builder.append(", type="); builder.append(this.type); - builder.append(", owner="); - builder.append(this.owner); builder.append(", status="); builder.append(this.status); + builder.append(", lastUpdateTime="); + builder.append(this.lastUpdateTime); + builder.append(", lastUpdateUser="); + builder.append(this.lastUpdateUser); builder.append("]"); return builder.toString(); } @@ -190,7 +214,9 @@ public final class ConfigurationNode implements GrantEntity { null, ConfigurationType.EXAM_CONFIG, null, - ConfigurationStatus.CONSTRUCTION); + ConfigurationStatus.CONSTRUCTION, + null, + null); } public static ConfigurationNode createNewTemplate(final Long institutionId) { @@ -202,7 +228,9 @@ public final class ConfigurationNode implements GrantEntity { null, ConfigurationType.TEMPLATE, null, - ConfigurationStatus.CONSTRUCTION); + ConfigurationStatus.CONSTRUCTION, + null, + null); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/SEBClientConfig.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/SEBClientConfig.java index b1020d93..0c61f1a6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/SEBClientConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/SEBClientConfig.java @@ -24,6 +24,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.model.Activatable; import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.Domain.CONFIGURATION_NODE; import ch.ethz.seb.sebserver.gbl.model.Domain.SEB_CLIENT_CONFIGURATION; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.GrantEntity; @@ -168,6 +169,12 @@ public final class SEBClientConfig implements GrantEntity, Activatable { @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE) public final Boolean active; + @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_LAST_UPDATE_TIME) + public final DateTime lastUpdateTime; + + @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_LAST_UPDATE_USER) + public final String lastUpdateUser; + @JsonCreator public SEBClientConfig( @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ID) final Long id, @@ -195,7 +202,9 @@ public final class SEBClientConfig implements GrantEntity, Activatable { @JsonProperty(ATTR_ENCRYPT_SECRET_CONFIRM) final CharSequence encryptSecretConfirm, @JsonProperty(ATTR_ENCRYPT_CERTIFICATE_ALIAS) final String encryptCertificateAlias, @JsonProperty(ATTR_ENCRYPT_CERTIFICATE_ASYM) final Boolean encryptCertificateAsym, - @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE) final Boolean active) { + @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE) final Boolean active, + @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_LAST_UPDATE_TIME) final DateTime lastUpdateTime, + @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_LAST_UPDATE_USER) final String lastUpdateUser) { this.id = id; this.institutionId = institutionId; @@ -229,6 +238,8 @@ public final class SEBClientConfig implements GrantEntity, Activatable { this.encryptCertificateAlias = encryptCertificateAlias; this.encryptCertificateAsym = encryptCertificateAsym; this.active = active; + this.lastUpdateTime = lastUpdateTime; + this.lastUpdateUser = lastUpdateUser; } public SEBClientConfig(final Long institutionId, final POSTMapper postParams) { @@ -268,6 +279,8 @@ public final class SEBClientConfig implements GrantEntity, Activatable { this.encryptCertificateAlias = postParams.getString(ATTR_ENCRYPT_CERTIFICATE_ALIAS); this.encryptCertificateAsym = postParams.getBooleanObject(ATTR_ENCRYPT_CERTIFICATE_ASYM); this.active = false; + this.lastUpdateTime = postParams.getDateTime(CONFIGURATION_NODE.ATTR_LAST_UPDATE_TIME); + this.lastUpdateUser = postParams.getString(CONFIGURATION_NODE.ATTR_LAST_UPDATE_USER); } @Override @@ -379,6 +392,38 @@ public final class SEBClientConfig implements GrantEntity, Activatable { return this.active; } + public Long getSebServerPingTime() { + return this.sebServerPingTime; + } + + public VDIType getVdiType() { + return this.vdiType; + } + + public String getVdiExecutable() { + return this.vdiExecutable; + } + + public String getVdiPath() { + return this.vdiPath; + } + + public String getVdiArguments() { + return this.vdiArguments; + } + + public Boolean getEncryptCertificateAsym() { + return this.encryptCertificateAsym; + } + + public DateTime getLastUpdateTime() { + return this.lastUpdateTime; + } + + public String getLastUpdateUser() { + return this.lastUpdateUser; + } + @Override public String toString() { final StringBuilder builder = new StringBuilder(); @@ -456,7 +501,9 @@ public final class SEBClientConfig implements GrantEntity, Activatable { Constants.EMPTY_NOTE, Constants.EMPTY_NOTE, this.encryptCertificateAsym, - this.active); + this.active, + this.lastUpdateTime, + this.lastUpdateUser); } public static SEBClientConfig createNew(final Long institutionId) { @@ -484,7 +531,9 @@ public final class SEBClientConfig implements GrantEntity, Activatable { null, null, false, - false); + false, + null, + null); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnection.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnection.java index 88796e21..e7ae3830 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnection.java @@ -8,8 +8,10 @@ package ch.ethz.seb.sebserver.gbl.model.session; +import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; +import java.util.List; import java.util.function.Predicate; import com.fasterxml.jackson.annotation.JsonCreator; @@ -46,6 +48,11 @@ public final class ClientConnection implements GrantEntity { } } + public final static List ACTIVE_STATES = Arrays.asList( + ConnectionStatus.ACTIVE.name(), + ConnectionStatus.AUTHENTICATED.name(), + ConnectionStatus.CONNECTION_REQUESTED.name()); + public static final ClientConnection EMPTY_CLIENT_CONNECTION = new ClientConnection( -1L, -1L, -1L, ConnectionStatus.UNDEFINED, @@ -60,6 +67,7 @@ public final class ClientConnection implements GrantEntity { public static final String FILTER_ATTR_SESSION_ID = Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID; public static final String FILTER_ATTR_IP_STRING = Domain.CLIENT_CONNECTION.ATTR_CLIENT_ADDRESS; public static final String FILTER_ATTR_INFO = ATTR_INFO; + public static final String FILTER_ATTR_TOKEN_LIST = "CONNECTION_TOKENS"; @JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID) public final Long id; @@ -381,7 +389,7 @@ public final class ClientConnection implements GrantEntity { } public static Predicate getStatusPredicate(final ConnectionStatus... status) { - final EnumSet states = EnumSet.allOf(ConnectionStatus.class); + final EnumSet states = EnumSet.noneOf(ConnectionStatus.class); if (status != null) { Collections.addAll(states, status); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnectionData.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnectionData.java index ddab83a6..a610f4c5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnectionData.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnectionData.java @@ -17,10 +17,14 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.GrantEntity; +import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.util.Utils; @JsonIgnoreProperties(ignoreUnknown = true) -public class ClientConnectionData { +public class ClientConnectionData implements GrantEntity { public static final String ATTR_CLIENT_CONNECTION = "cData"; public static final String ATTR_INDICATOR_VALUE = "iValues"; @@ -48,7 +52,7 @@ public class ClientConnectionData { this.indicatorValues = Utils.immutableListOf(indicatorValues); } - protected ClientConnectionData( + public ClientConnectionData( final ClientConnection clientConnection, final List indicatorValues) { @@ -58,6 +62,26 @@ public class ClientConnectionData { this.indicatorValues = Utils.immutableListOf(indicatorValues); } + @Override + public EntityType entityType() { + return this.clientConnection.entityType(); + } + + @Override + public String getName() { + return this.clientConnection.getName(); + } + + @Override + public String getModelId() { + return this.clientConnection.getModelId(); + } + + @Override + public Long getInstitutionId() { + return this.clientConnection.getInstitutionId(); + } + @JsonProperty(ATTR_MISSING_PING) public Boolean getMissingPing() { return this.missingPing; @@ -78,6 +102,26 @@ public class ClientConnectionData { return this.missingPing || this.pendingNotification; } + @JsonIgnore + public Double getIndicatorValue(final Long indicatorId) { + return this.indicatorValues + .stream() + .filter(indicatorValue -> indicatorValue.getIndicatorId().equals(indicatorId)) + .findFirst() + .map(iv -> iv.getValue()) + .orElse(Double.NaN); + } + + @JsonIgnore + public String getIndicatorDisplayValue(final Indicator indicator) { + return this.indicatorValues + .stream() + .filter(indicatorValue -> indicatorValue.getIndicatorId().equals(indicator.id)) + .findFirst() + .map(iv -> IndicatorValue.getDisplayValue(iv, indicator.type)) + .orElse(Constants.EMPTY_NOTE); + } + public ClientConnection getClientConnection() { return this.clientConnection; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientInstruction.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientInstruction.java index aa111164..e9ca8908 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientInstruction.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientInstruction.java @@ -27,7 +27,8 @@ public final class ClientInstruction { SEB_QUIT, SEB_PROCTORING, SEB_RECONFIGURE_SETTINGS, - NOTIFICATION_CONFIRM + NOTIFICATION_CONFIRM, + SEB_FORCE_LOCK_SCREEN } public enum ProctoringInstructionMethod { @@ -70,6 +71,11 @@ public final class ClientInstruction { public static final String ZOOM_RECEIVE_VIDEO = "zoomReceiveVideo"; public static final String ZOOM_ALLOW_CHAT = "zoomFeatureFlagChat"; } + + public interface SEB_FORCE_LOCK_SCREEN { + public static final String MESSAGE = "message"; + public static final String IMAGE_URL = "imageURL"; + } } @JsonProperty(Domain.CLIENT_INSTRUCTION.ATTR_ID) diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/IndicatorValue.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/IndicatorValue.java index ef817f5e..3569e254 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/IndicatorValue.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/IndicatorValue.java @@ -32,7 +32,7 @@ public interface IndicatorValue extends IndicatorValueHolder { return Constants.EMPTY_NOTE; } if (type.integerValue) { - return String.valueOf((int) indicatorValue.getValue()); + return String.valueOf((long) indicatorValue.getValue()); } else { return String.valueOf(indicatorValue.getValue()); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/RunningExamInfo.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/RunningExamInfo.java index 627029bd..d12db3c1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/RunningExamInfo.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/RunningExamInfo.java @@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gbl.model.session; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; @@ -46,8 +47,8 @@ public final class RunningExamInfo { public RunningExamInfo(final Exam exam, final LmsType lmsType) { this.examId = exam.getModelId(); this.name = exam.name; - this.url = exam.startURL; - this.lmsType = (lmsType == null) ? "" : lmsType.name(); + this.url = exam.getStartURL(); + this.lmsType = (lmsType == null) ? Constants.EMPTY_NOTE : lmsType.name(); } public String getExamId() { diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserLogActivityType.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserLogActivityType.java index 777140a9..1ad5c6d6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserLogActivityType.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserLogActivityType.java @@ -18,6 +18,7 @@ public enum UserLogActivityType { PASSWORD_CHANGE, DEACTIVATE, ACTIVATE, + FINISHED, DELETE, LOGIN, LOGOUT diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Result.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Result.java index 956e1b9b..e2aa8d8f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Result.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Result.java @@ -262,7 +262,7 @@ public final class Result { public Result onErrorDo(final Function errorHandler) { if (this.error != null) { - return new Result<>(errorHandler.apply(this.error)); + return Result.tryCatch(() -> errorHandler.apply(this.error)); } return this; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java index 5032e6b2..18f1ed69 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java @@ -50,9 +50,11 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.JSONMapper; public final class Utils { @@ -246,6 +248,9 @@ public final class Utils { } public static String formatDate(final DateTime dateTime) { + if (dateTime == null) { + return Constants.EMPTY_NOTE; + } return dateTime.toString(Constants.STANDARD_DATE_TIME_MILLIS_FORMATTER); } @@ -576,6 +581,9 @@ public final class Utils { * @param rgb foreground or text color * @return true of the background color for given foreground color shall be dark or false if it shall be light */ public static boolean darkColorContrast(final RGB rgb) { + if (rgb == null) { + return true; + } return rgb.red + rgb.green + rgb.blue > DARK_COLOR_THRESHOLD; } @@ -590,15 +598,16 @@ public final class Utils { } public static RGB parseRGB(final String colorString) { - if (StringUtils.isBlank(colorString)) { + try { + + final int r = Integer.parseInt(colorString.substring(0, 2), 16); + final int g = Integer.parseInt(colorString.substring(2, 4), 16); + final int b = Integer.parseInt(colorString.substring(4, 6), 16); + + return new RGB(r, g, b); + } catch (final Exception e) { return null; } - - final int r = Integer.parseInt(colorString.substring(0, 2), 16); - final int g = Integer.parseInt(colorString.substring(2, 4), 16); - final int b = Integer.parseInt(colorString.substring(4, 6), 16); - - return new RGB(r, g, b); } public static String toColorFractionString(final int fraction) { @@ -735,4 +744,17 @@ public final class Utils { return builder; } + public static Map jsonToMap(final String attribute, final JSONMapper mapper) { + if (StringUtils.isBlank(attribute)) { + return Collections.emptyMap(); + } + try { + return mapper.readValue(attribute, new TypeReference>() { + }); + } catch (final Exception e) { + log.error("Failed to parse json to map: ", e); + return Collections.emptyMap(); + } + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/GuiServiceInfo.java b/src/main/java/ch/ethz/seb/sebserver/gui/GuiServiceInfo.java index b45c66c5..48a46e95 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/GuiServiceInfo.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/GuiServiceInfo.java @@ -19,6 +19,7 @@ import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; @GuiProfile public class GuiServiceInfo { + private final String sebServerVersion; private final String externalScheme; private final String internalServer; private final String externalServer; @@ -29,8 +30,10 @@ public class GuiServiceInfo { private final UriComponentsBuilder internalServerURIBuilder; private final UriComponentsBuilder externalServerURIBuilder; private final boolean distributedSetup; + private final boolean multilingualGUI; public GuiServiceInfo( + @Value("${sebserver.version:--}") final String sebServerVersion, @Value("${server.address}") final String internalServer, @Value("${server.port}") final String internalPort, @Value("${sebserver.gui.http.external.scheme}") final String externalScheme, @@ -38,7 +41,8 @@ public class GuiServiceInfo { @Value("${sebserver.gui.http.external.port}") final String externalPort, @Value("${sebserver.gui.entrypoint:/gui}") final String entryPoint, @Value("${server.servlet.context-path:/}") final String contextPath, - @Value("${sebserver.webservice.distributed:false}") final boolean distributedSetup) { + @Value("${sebserver.webservice.distributed:false}") final boolean distributedSetup, + @Value("${sebserver.gui.multilingual:false}") final boolean multilingualGUI) { if (StringUtils.isBlank(externalScheme)) { throw new RuntimeException("Missing mandatory inital parameter sebserver.gui.http.external.servername"); @@ -48,6 +52,7 @@ public class GuiServiceInfo { throw new RuntimeException("Missing mandatory inital parameter sebserver.gui.http.external.servername"); } + this.sebServerVersion = sebServerVersion; this.externalScheme = externalScheme; this.internalServer = internalServer; this.externalServer = externalServer; @@ -73,6 +78,7 @@ public class GuiServiceInfo { } this.distributedSetup = distributedSetup; + this.multilingualGUI = multilingualGUI; } public String getExternalScheme() { @@ -115,4 +121,12 @@ public class GuiServiceInfo { return this.distributedSetup; } + public String getSebServerVersion() { + return this.sebServerVersion; + } + + public boolean isMultilingualGUI() { + return this.multilingualGUI; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/GuiWebsecurityConfig.java b/src/main/java/ch/ethz/seb/sebserver/gui/GuiWebsecurityConfig.java index 0c4a61a0..b0d685c9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/GuiWebsecurityConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/GuiWebsecurityConfig.java @@ -37,6 +37,8 @@ public class GuiWebsecurityConfig extends WebSecurityConfigurerAdapter { private String remoteProctoringEndpoint; @Value("${sebserver.gui.remote.proctoring.api-servler.endpoint:/remote-view-servlet}") private String remoteProctoringViewServletEndpoint; + @Value("${springdoc.api-docs.enabled:false}") + private boolean springDocsAPIEnabled; /** Gui-service related public URLS from spring web security perspective */ public static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher( @@ -57,6 +59,10 @@ public class GuiWebsecurityConfig extends WebSecurityConfigurerAdapter { .antMatchers(this.guiEntryPoint) .antMatchers(this.remoteProctoringEndpoint) .antMatchers(this.remoteProctoringEndpoint + this.remoteProctoringViewServletEndpoint + "/*"); + + if (this.springDocsAPIEnabled) { + web.ignoring().antMatchers("/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**"); + } } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/InstitutionalAuthenticationEntryPoint.java b/src/main/java/ch/ethz/seb/sebserver/gui/InstitutionalAuthenticationEntryPoint.java index 8971254b..12d24351 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/InstitutionalAuthenticationEntryPoint.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/InstitutionalAuthenticationEntryPoint.java @@ -231,7 +231,7 @@ public final class InstitutionalAuthenticationEntryPoint implements Authenticati final Object attribute = RWT.getUISession().getHttpSession().getAttribute(INST_SUFFIX_ATTRIBUTE); return (attribute != null) ? String.valueOf(attribute) : null; } catch (final Exception e) { - log.warn("Failed to extract institutional endpoint form user session: {}", e.getMessage()); + log.warn("Failed to extract institutional endpoint from user session: {}", e.getMessage()); return null; } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionCategory.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionCategory.java index ad3a207c..6594f68e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionCategory.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionCategory.java @@ -30,12 +30,20 @@ public enum ActionCategory { EXAM_MONITORING_NOTIFICATION_LIST(new LocTextKey( "sebserver.monitoring.exam.connection.notificationlist.actions"), 1), + EXAM_MONITORING_2(new LocTextKey( + "sebserver.monitoring.exam.connection.actions.group2"), + 2), + EXAM_MONITORING_3(new LocTextKey( + "sebserver.monitoring.exam.connection.actions.group3"), + 3), CLIENT_EVENT_LIST(new LocTextKey("sebserver.monitoring.exam.connection.list.actions"), 1), LOGS_USER_ACTIVITY_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1), LOGS_SEB_CLIENT_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1), VARIA(new LocTextKey("sebserver.overall.action.category.varia"), 0), FILTER(new LocTextKey("sebserver.exam.monitoring.action.category.filter"), 50), - PROCTORING(new LocTextKey("sebserver.exam.overall.action.category.proctoring"), 60); + PROCTORING(new LocTextKey("sebserver.exam.overall.action.category.proctoring"), 60), + + FINISHED_EXAM_LIST(new LocTextKey("sebserver.finished.exam.list.actions"), 1); public final LocTextKey title; public final int slotPosition; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java index 2922a097..586e478c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java @@ -73,6 +73,11 @@ public enum ActionDefinition { ImageIcon.SWITCH, PageStateDefinitionImpl.INSTITUTION_LIST, ActionCategory.INSTITUTION_LIST), + INSTITUTION_DELETE( + new LocTextKey("sebserver.institution.action.delete"), + ImageIcon.DELETE, + PageStateDefinitionImpl.INSTITUTION_VIEW, + ActionCategory.FORM), USER_ACCOUNT_VIEW_LIST( new LocTextKey("sebserver.useraccount.action.list"), @@ -215,6 +220,11 @@ public enum ActionDefinition { ImageIcon.SWITCH, PageStateDefinitionImpl.LMS_SETUP_LIST, ActionCategory.LMS_SETUP_LIST), + LMS_SETUP_DELETE( + new LocTextKey("sebserver.lmssetup.action.delete"), + ImageIcon.DELETE, + PageStateDefinitionImpl.LMS_SETUP_VIEW, + ActionCategory.FORM), QUIZ_DISCOVERY_VIEW_LIST( new LocTextKey("sebserver.quizdiscovery.action.list"), @@ -281,6 +291,11 @@ public enum ActionDefinition { ImageIcon.DELETE, PageStateDefinitionImpl.EXAM_VIEW, ActionCategory.FORM), + EXAM_ARCHIVE( + new LocTextKey("sebserver.exam.action.archive"), + ImageIcon.ARCHIVE, + PageStateDefinitionImpl.EXAM_VIEW, + ActionCategory.FORM), EXAM_MODIFY_SEB_RESTRICTION_DETAILS( new LocTextKey("sebserver.exam.action.sebrestriction.details"), @@ -417,6 +432,16 @@ public enum ActionDefinition { ImageIcon.DELETE, PageStateDefinitionImpl.EXAM_TEMPLATE_LIST, ActionCategory.FORM), + EXAM_TEMPLATE_PROCTORING_ON( + new LocTextKey("sebserver.examtemplate.proctoring.actions.open"), + ImageIcon.VISIBILITY, + PageStateDefinitionImpl.EXAM_TEMPLATE_VIEW, + ActionCategory.FORM), + EXAM_TEMPLATE_PROCTORING_OFF( + new LocTextKey("sebserver.examtemplate.proctoring.actions.open"), + ImageIcon.VISIBILITY_OFF, + PageStateDefinitionImpl.EXAM_TEMPLATE_VIEW, + ActionCategory.FORM), INDICATOR_TEMPLATE_NEW( new LocTextKey("sebserver.examtemplate.indicator.action.list.new"), @@ -502,6 +527,16 @@ public enum ActionDefinition { ImageIcon.EXPORT, PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, ActionCategory.FORM), + SEB_CLIENT_CONFIG_DELETE( + new LocTextKey("sebserver.clientconfig.action.delete"), + ImageIcon.DELETE, + PageStateDefinitionImpl.SEB_CLIENT_CONFIG_LIST, + ActionCategory.FORM), + SEB_CLIENT_CONFIG_SHOW_CREDENTIALS( + new LocTextKey("sebserver.clientconfig.action.credentials"), + ImageIcon.SECURE, + PageStateDefinitionImpl.SEB_CLIENT_CONFIG_VIEW, + ActionCategory.FORM), SEB_EXAM_CONFIG_LIST( new LocTextKey("sebserver.examconfig.action.list"), @@ -527,6 +562,18 @@ public enum ActionDefinition { PageStateDefinitionImpl.SEB_EXAM_CONFIG_LIST, ActionCategory.LIST_VARIA), + SEB_EXAM_CONFIG_BULK_STATE_CHANGE( + new LocTextKey("sebserver.examconfig.list.action.statechange"), + ImageIcon.SWITCH, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_LIST, + ActionCategory.SEB_EXAM_CONFIG_LIST), + + SEB_EXAM_CONFIG_BULK_RESET_TO_TEMPLATE( + new LocTextKey("sebserver.examconfig.list.action.reset"), + ImageIcon.EXPORT, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_LIST, + ActionCategory.SEB_EXAM_CONFIG_LIST), + SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST( new LocTextKey("sebserver.examconfig.action.list.modify.properties"), ImageIcon.EDIT, @@ -580,8 +627,12 @@ public enum ActionDefinition { SEB_EXAM_CONFIG_COPY_CONFIG_FROM_LIST( new LocTextKey("sebserver.examconfig.action.copy"), ImageIcon.COPY, - // PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_EDIT, ActionCategory.SEB_EXAM_CONFIG_LIST), + + SEB_EXAM_CONFIG_RESET_TO_TEMPLATE_SETTINGS( + new LocTextKey("sebserver.examconfig.action.restore.template.settings"), + ImageIcon.EXPORT, + ActionCategory.FORM), SEB_EXAM_CONFIG_COPY_CONFIG( new LocTextKey("sebserver.examconfig.action.copy"), ImageIcon.COPY, @@ -644,6 +695,11 @@ public enum ActionDefinition { ImageIcon.SAVE, PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_VIEW, ActionCategory.FORM), + SEB_EXAM_CONFIG_TEMPLATE_DELETE( + new LocTextKey("sebserver.configtemplate.action.delete"), + ImageIcon.DELETE, + PageStateDefinitionImpl.SEB_EXAM_CONFIG_TEMPLATE_LIST, + ActionCategory.FORM), SEB_EXAM_CONFIG_TEMPLATE_ATTR_EDIT( new LocTextKey("sebserver.configtemplate.attr.list.actions.modify"), @@ -713,12 +769,18 @@ public enum ActionDefinition { new LocTextKey("sebserver.monitoring.exam.connection.action.view"), ImageIcon.SHOW, PageStateDefinitionImpl.MONITORING_CLIENT_CONNECTION, - ActionCategory.CLIENT_EVENT_LIST), + ActionCategory.EXAM_MONITORING_3), MONITOR_EXAM_CLIENT_CONNECTION_QUIT( new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit"), ImageIcon.SEND_QUIT, PageStateDefinitionImpl.MONITORING_CLIENT_CONNECTION, ActionCategory.FORM), + MONITOR_EXAM_CLIENT_CONNECTION_LOCK( + new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.lock"), + ImageIcon.LOCK, + PageStateDefinitionImpl.MONITORING_CLIENT_CONNECTION, + ActionCategory.FORM), + MONITOR_EXAM_CLIENT_CONNECTION_PROCTORING( new LocTextKey("sebserver.monitoring.exam.connection.action.proctoring"), ImageIcon.PROCTOR_SINGLE, @@ -739,12 +801,18 @@ public enum ActionDefinition { new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit.selected"), ImageIcon.SEND_QUIT, PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, - ActionCategory.CLIENT_EVENT_LIST), + ActionCategory.EXAM_MONITORING_2), MONITOR_EXAM_QUIT_ALL( new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit.all"), ImageIcon.SEND_QUIT, PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, - ActionCategory.FORM), + ActionCategory.EXAM_MONITORING_2), + MONITOR_EXAM_LOCK_SELECTED( + new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.lock.selected"), + ImageIcon.LOCK, + PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, + ActionCategory.EXAM_MONITORING_2), + MONITOR_EXAM_BACK_TO_OVERVIEW( new LocTextKey("sebserver.monitoring.exam.action.detail.view"), ImageIcon.SHOW, @@ -755,7 +823,7 @@ public enum ActionDefinition { new LocTextKey("sebserver.monitoring.exam.connection.action.disable"), ImageIcon.DISABLE, PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, - ActionCategory.CLIENT_EVENT_LIST), + ActionCategory.EXAM_MONITORING_3), MONITOR_EXAM_HIDE_REQUESTED_CONNECTION( new LocTextKey("sebserver.monitoring.exam.connection.action.hide.requested"), @@ -832,6 +900,25 @@ public enum ActionDefinition { PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, ActionCategory.PROCTORING), + FINISHED_EXAM_VIEW_LIST( + new LocTextKey("sebserver.finished.action.list"), + PageStateDefinitionImpl.FINISHED_EXAM_LIST), + VIEW_FINISHED_EXAM_FROM_LIST( + new LocTextKey("sebserver.finished.exam.action.list.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.FINISHED_EXAM, + ActionCategory.FINISHED_EXAM_LIST), + VIEW_FINISHED_EXAM_CLIENT_CONNECTION( + new LocTextKey("sebserver.finished.exam.connection.action.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.FINISHED_CLIENT_CONNECTION, + ActionCategory.CLIENT_EVENT_LIST), + FINISHED_EXAM_BACK_TO_OVERVIEW( + new LocTextKey("sebserver.finished.exam.action.detail.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.FINISHED_EXAM, + ActionCategory.FORM), + LOGS_USER_ACTIVITY_LIST( new LocTextKey("sebserver.logs.activity.userlogs"), PageStateDefinitionImpl.USER_ACTIVITY_LOGS), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java index de67612f..9f791680 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java @@ -238,6 +238,9 @@ public class ActionPane implements TemplateComposer { CustomVariant.TEXT_H3, category.title); final GridData titleLayout = new GridData(SWT.FILL, SWT.TOP, true, false); + if (" ".equals(this.pageService.getI18nSupport().getText(category.title))) { + titleLayout.heightHint = 6; + } actionsTitle.setLayoutData(titleLayout); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java index 35b35bd0..7a07a2f3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java @@ -339,14 +339,25 @@ public class ActivitiesPane implements TemplateComposer { // Monitoring exams if (isSupporter) { - final TreeItem clientConfig = this.widgetFactory.treeItemLocalized( + + final TreeItem monitoringExams = this.widgetFactory.treeItemLocalized( monitoring, ActivityDefinition.MONITORING_EXAMS.displayName); injectActivitySelection( - clientConfig, + monitoringExams, actionBuilder .newAction(ActionDefinition.RUNNING_EXAM_VIEW_LIST) .create()); + + final TreeItem clientConfig = this.widgetFactory.treeItemLocalized( + monitoring, + ActivityDefinition.FINISHED_EXAMS.displayName); + injectActivitySelection( + clientConfig, + actionBuilder + .newAction(ActionDefinition.FINISHED_EXAM_VIEW_LIST) + .create()); + } // SEB Client Logs diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivityDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivityDefinition.java index a8d57e0d..190cc77f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivityDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivityDefinition.java @@ -28,6 +28,7 @@ public enum ActivityDefinition implements Activity { SEB_CERTIFICATE_MANAGEMENT(new LocTextKey("sebserver.certificate.action.list")), MONITORING(new LocTextKey("sebserver.overall.activity.title.monitoring")), MONITORING_EXAMS(new LocTextKey("sebserver.monitoring.action.list")), + FINISHED_EXAMS(new LocTextKey("sebserver.finished.action.list")), SEB_CLIENT_LOGS(new LocTextKey("sebserver.logs.activity.seblogs")); public final LocTextKey displayName; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java index 54b5d28e..2a485fd2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java @@ -33,6 +33,9 @@ import ch.ethz.seb.sebserver.gui.content.exam.IndicatorTemplateForm; import ch.ethz.seb.sebserver.gui.content.exam.LmsSetupForm; import ch.ethz.seb.sebserver.gui.content.exam.LmsSetupList; import ch.ethz.seb.sebserver.gui.content.exam.QuizLookupList; +import ch.ethz.seb.sebserver.gui.content.monitoring.FinishedExam; +import ch.ethz.seb.sebserver.gui.content.monitoring.FinishedExamClientConnection; +import ch.ethz.seb.sebserver.gui.content.monitoring.FinishedExamList; import ch.ethz.seb.sebserver.gui.content.monitoring.MonitoringClientConnection; import ch.ethz.seb.sebserver.gui.content.monitoring.MonitoringRunningExam; import ch.ethz.seb.sebserver.gui.content.monitoring.MonitoringRunningExamList; @@ -96,6 +99,10 @@ public enum PageStateDefinitionImpl implements PageStateDefinition { MONITORING_RUNNING_EXAM(Type.FORM_VIEW, MonitoringRunningExam.class, ActivityDefinition.MONITORING_EXAMS), MONITORING_CLIENT_CONNECTION(Type.FORM_VIEW, MonitoringClientConnection.class, ActivityDefinition.MONITORING_EXAMS), + FINISHED_EXAM_LIST(Type.LIST_VIEW, FinishedExamList.class, ActivityDefinition.FINISHED_EXAMS), + FINISHED_EXAM(Type.FORM_VIEW, FinishedExam.class, ActivityDefinition.FINISHED_EXAMS), + FINISHED_CLIENT_CONNECTION(Type.FORM_VIEW, FinishedExamClientConnection.class, ActivityDefinition.FINISHED_EXAMS), + USER_ACTIVITY_LOGS(Type.LIST_VIEW, UserActivityLogs.class, ActivityDefinition.USER_ACTIVITY_LOGS), SEB_CLIENT_LOGS(Type.LIST_VIEW, SEBClientEvents.class, ActivityDefinition.SEB_CLIENT_LOGS) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionDeletePopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionDeletePopup.java new file mode 100644 index 00000000..93b02fbc --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionDeletePopup.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2020 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.admin; + +import java.util.List; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage; +import ch.ethz.seb.sebserver.gbl.model.EntityDependency; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; +import ch.ethz.seb.sebserver.gbl.model.institution.Institution; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageMessageException; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; +import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputWizard; +import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputWizard.WizardAction; +import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputWizard.WizardPage; +import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.DeleteInstitution; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionDependency; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; + +@Lazy +@Component +@GuiProfile +public class InstitutionDeletePopup { + + private static final Logger log = LoggerFactory.getLogger(InstitutionDeletePopup.class); + + private final static LocTextKey FORM_TITLE = + new LocTextKey("sebserver.institution.delete.form.title"); + private final static LocTextKey FORM_INFO = + new LocTextKey("sebserver.institution.delete.form.info"); + private final static LocTextKey FORM_REPORT_INFO = + new LocTextKey("sebserver.institution.delete.report.info"); + private final static LocTextKey FORM_REPORT_LIST_TYPE = + new LocTextKey("sebserver.institution.delete.report.list.type"); + private final static LocTextKey FORM_REPORT_LIST_NAME = + new LocTextKey("sebserver.institution.delete.report.list.name"); + private final static LocTextKey FORM_REPORT_LIST_DESC = + new LocTextKey("sebserver.institution.delete.report.list.description"); + private final static LocTextKey FORM_REPORT_NONE = + new LocTextKey("sebserver.institution.delete.report.list.empty"); + + private final static LocTextKey ACTION_DELETE = + new LocTextKey("sebserver.institution.delete.action.delete"); + + private final static LocTextKey DELETE_CONFIRM_TITLE = + new LocTextKey("sebserver.institution.delete.confirm.title"); + private final static LocTextKey DELETE_ERROR_CONSISTENCY = + new LocTextKey("sebserver.institution.action.delete.consistency.error"); + + private final PageService pageService; + + protected InstitutionDeletePopup(final PageService pageService) { + this.pageService = pageService; + } + + public Function deleteWizardFunction(final PageContext pageContext) { + return action -> { + + final ModalInputWizard wizard = + new ModalInputWizard( + action.pageContext().getParent().getShell(), + this.pageService.getWidgetFactory()) + .setVeryLargeDialogWidth(); + + final String page1Id = "DELETE_PAGE"; + final Predicate callback = pc -> doDelete(this.pageService, pc); + final BiFunction> composePage1 = + (prefPageContext, content) -> composeDeleteDialog(content, + (prefPageContext != null) ? prefPageContext : pageContext); + + final WizardPage page1 = new WizardPage<>( + page1Id, + true, + composePage1, + new WizardAction<>(ACTION_DELETE, callback)); + + wizard.open(FORM_TITLE, Utils.EMPTY_EXECUTION, page1); + + return action; + }; + } + + private boolean doDelete( + final PageService pageService, + final PageContext pageContext) { + + try { + final EntityKey entityKey = pageContext.getEntityKey(); + final Institution toDelete = this.pageService + .getRestService() + .getBuilder(GetInstitution.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .getOrThrow(); + + final RestCall.RestCallBuilder restCallBuilder = this.pageService.getRestService() + .getBuilder(DeleteInstitution.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name()); + + final Result deleteCall = restCallBuilder.call(); + if (deleteCall.hasError()) { + final Exception error = deleteCall.getError(); + if (error instanceof RestCallError) { + final APIMessage message = ((RestCallError) error) + .getAPIMessages() + .stream() + .findFirst() + .orElse(null); + if (message != null && ErrorMessage.INTEGRITY_VALIDATION.isOf(message)) { + pageContext.publishPageMessage(new PageMessageException(DELETE_ERROR_CONSISTENCY)); + return false; + } + } + } + + final EntityProcessingReport report = deleteCall.getOrThrow(); + + final PageAction action = this.pageService.pageActionBuilder(pageContext) + .newAction(ActionDefinition.INSTITUTION_VIEW_LIST) + .create(); + + this.pageService.firePageEvent( + new ActionEvent(action), + action.pageContext()); + + final List dependencies = report.results.stream() + .filter(key -> !key.equals(entityKey)) + .collect(Collectors.toList()); + pageContext.publishPageMessage( + DELETE_CONFIRM_TITLE, + new LocTextKey( + "sebserver.institution.delete.confirm.message", + toDelete.toName().name, + dependencies.size(), + (report.errors.isEmpty()) ? "no" : String.valueOf((report.errors.size())))); + return true; + } catch (final Exception e) { + log.error("Unexpected error while trying to delete Institution:", e); + pageContext.notifyUnexpectedError(e); + return false; + } + } + + private Supplier composeDeleteDialog( + final Composite parent, + final PageContext pageContext) { + + final I18nSupport i18nSupport = this.pageService.getI18nSupport(); + final Composite grid = this.pageService.getWidgetFactory() + .createPopupScrollComposite(parent); + + final Label title = this.pageService.getWidgetFactory() + .labelLocalized(grid, CustomVariant.TEXT_H3, FORM_INFO); + final GridData gridData = new GridData(); + gridData.horizontalIndent = 10; + gridData.verticalIndent = 10; + title.setLayoutData(gridData); + + final Label titleReport = this.pageService.getWidgetFactory() + .labelLocalized(grid, CustomVariant.TEXT_H3, FORM_REPORT_INFO); + final GridData gridDataReport = new GridData(); + gridDataReport.horizontalIndent = 10; + gridDataReport.verticalIndent = 10; + titleReport.setLayoutData(gridDataReport); + + try { + + // get dependencies + final EntityKey entityKey = pageContext.getEntityKey(); + final RestCall>.RestCallBuilder restCallBuilder = this.pageService.getRestService() + .getBuilder(GetInstitutionDependency.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name()); + + final Set dependencies = restCallBuilder + .call() + .getOrThrow(); + final List list = dependencies + .stream() + .sorted() + .collect(Collectors.toList()); + + this.pageService. staticListTableBuilder(list, null) + .withEmptyMessage(FORM_REPORT_NONE) + .withColumn(new ColumnDefinition<>( + "FORM_REPORT_LIST_TYPE", + FORM_REPORT_LIST_TYPE, + dep -> i18nSupport + .getText("sebserver.overall.types.entityType." + dep.self.entityType.name()))) + .withColumn(new ColumnDefinition<>( + "FORM_REPORT_LIST_NAME", + FORM_REPORT_LIST_NAME, + dep -> dep.name)) + .withColumn(new ColumnDefinition( + "FORM_REPORT_LIST_DESC", + FORM_REPORT_LIST_DESC, + dep -> dep.description)) + .compose(pageContext.copyOf(grid)); + + return () -> pageContext; + } catch (final Exception e) { + log.error("Error while trying to compose Institution delete report page: ", e); + pageContext.notifyUnexpectedError(e); + throw e; + } + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionForm.java index 321e74ac..cd1b7ad0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionForm.java @@ -56,12 +56,16 @@ public class InstitutionForm implements TemplateComposer { private final PageService pageService; private final RestService restService; private final CurrentUser currentUser; + private final InstitutionDeletePopup institutionDeletePopup; - protected InstitutionForm(final PageService pageService) { + protected InstitutionForm( + final PageService pageService, + final InstitutionDeletePopup institutionDeletePopup) { this.pageService = pageService; this.restService = pageService.getRestService(); this.currentUser = pageService.getCurrentUser(); + this.institutionDeletePopup = institutionDeletePopup; } @Override @@ -132,6 +136,11 @@ public class InstitutionForm implements TemplateComposer { .withEntityKey(entityKey) .publishIf(() -> modifyGrant && isReadonly) + .newAction(ActionDefinition.INSTITUTION_DELETE) + .withEntityKey(entityKey) + .withExec(this.institutionDeletePopup.deleteWizardFunction(pageContext)) + .publishIf(() -> writeGrant && isReadonly) + .newAction(ActionDefinition.INSTITUTION_DEACTIVATE) .withEntityKey(entityKey) .withSimpleRestCall(this.restService, DeactivateInstitution.class) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionList.java index 55f18947..24f3053e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/InstitutionList.java @@ -138,14 +138,14 @@ public class InstitutionList implements TemplateComposer { .newAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST) .withSelect( - table::getSelection, + table::getMultiSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) .publish(false) .newAction(ActionDefinition.INSTITUTION_MODIFY_FROM_LIST) .withSelect( - table::getSelection, + table::getMultiSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> instGrant.m(), false) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserAccountChangePasswordForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserAccountChangePasswordForm.java index a7da5611..fb120c63 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserAccountChangePasswordForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserAccountChangePasswordForm.java @@ -54,6 +54,8 @@ public class UserAccountChangePasswordForm implements TemplateComposer { new LocTextKey("sebserver.useraccount.form.password.new.confirm"); private static final LocTextKey FORM_PASSWORD_TEXT_KEY = new LocTextKey("sebserver.useraccount.form.password"); + private static final String PASSWORD_CHANGE_INFO_KEY = + "sebserver.useraccount.form.password.info"; private final PageService pageService; private final RestService restService; @@ -85,6 +87,11 @@ public class UserAccountChangePasswordForm implements TemplateComposer { pageContext.getParent(), new LocTextKey(FORM_TITLE_KEY, userInfo.username)); + widgetFactory.labelLocalized( + content, + WidgetFactory.CustomVariant.SUBTITLE, + new LocTextKey(PASSWORD_CHANGE_INFO_KEY, userInfo.username)); + final boolean ownAccount = this.currentUser.get().uuid.equals(entityKey.getModelId()); // The Password Change form diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserAccountList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserAccountList.java index d6790fca..c6032e0b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserAccountList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserAccountList.java @@ -222,11 +222,12 @@ public class UserAccountList implements TemplateComposer { .publishIf(userGrant::iw) .newAction(ActionDefinition.USER_ACCOUNT_VIEW_FROM_LIST) - .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) + .withSelect(table::getMultiSelection, PageAction::applySingleSelectionAsEntityKey, + EMPTY_SELECTION_TEXT_KEY) .publish(false) .newAction(ActionDefinition.USER_ACCOUNT_MODIFY_FROM_LIST) - .withSelect(table::getSelection, this::editAction, EMPTY_SELECTION_TEXT_KEY) + .withSelect(table::getMultiSelection, this::editAction, EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> userGrant.im(), false) .newAction(ActionDefinition.USER_ACCOUNT_TOGGLE_ACTIVITY) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserActivityLogs.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserActivityLogs.java index 3da98473..cfd870d9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserActivityLogs.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/admin/UserActivityLogs.java @@ -244,7 +244,7 @@ public class UserActivityLogs implements TemplateComposer { actionBuilder .newAction(ActionDefinition.LOGS_USER_ACTIVITY_SHOW_DETAILS) .withSelect( - table::getSelection, + table::getMultiSelection, action -> this.showDetails(action, table.getSingleSelectedROWData()), EMPTY_SELECTION_TEXT) .noEventPropagation() diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/CertificateImportPopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/CertificateImportPopup.java index b8d4f872..859544c3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/CertificateImportPopup.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/CertificateImportPopup.java @@ -227,10 +227,6 @@ public class CertificateImportPopup { null, CertificateFileType.getAllExtensions())) -// .addField(FormBuilder.text( -// CertificateInfo.ATTR_ALIAS, -// CertificateList.FORM_ALIAS_TEXT_KEY)) - .addField(FormBuilder.text( API.IMPORT_PASSWORD_ATTR_NAME, CertificateList.FORM_IMPORT_PASSWORD_TEXT_KEY, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/CertificateList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/CertificateList.java index f887aea6..cc508c0a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/CertificateList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/CertificateList.java @@ -159,7 +159,7 @@ public class CertificateList implements TemplateComposer { .newAction(ActionDefinition.SEB_CERTIFICATE_REMOVE) .withConfirm(() -> FORM_ACTION_MESSAGE_REMOVE_CONFIRM_TEXT_KEY) .withSelect( - table::getSelection, + table::getMultiSelection, this::removeCertificate, EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> grantCheck.iw(), false); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/ConfigTemplateForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/ConfigTemplateForm.java index d332ddb9..df2c10a7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/ConfigTemplateForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/ConfigTemplateForm.java @@ -8,6 +8,10 @@ package ch.ethz.seb.sebserver.gui.content.configs; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + import org.apache.commons.lang3.StringUtils; import org.eclipse.swt.widgets.Composite; import org.springframework.context.annotation.Lazy; @@ -18,12 +22,17 @@ import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; +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.ConfigurationValue; import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.form.FormBuilder; import ch.ethz.seb.sebserver.gui.form.FormHandle; @@ -37,6 +46,9 @@ import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.DeleteExamConfiguration; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurationValues; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurations; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetTemplateAttributePage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.NewExamConfig; @@ -72,11 +84,23 @@ public class ConfigTemplateForm implements TemplateComposer { new LocTextKey("sebserver.configtemplate.attrs.list.view"); private static final LocTextKey ATTRIBUTES_LIST_GROUP_TEXT_KEY = new LocTextKey("sebserver.configtemplate.attrs.list.group"); + private static final LocTextKey ATTRIBUTES_LIST_VALUE_TEXT_KEY = + new LocTextKey("sebserver.configtemplate.attrs.list.value"); private static final LocTextKey ATTRIBUTES_LIST_TYPE_TEXT_KEY = new LocTextKey("sebserver.configtemplate.attrs.list.type"); private static final LocTextKey EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY = new LocTextKey("sebserver.configtemplate.attr.info.pleaseSelect"); + static final LocTextKey CONFIRM_DELETE = + new LocTextKey("sebserver.configtemplate.message.confirm.delete"); + static final LocTextKey DELETE_CONFIRM_TITLE = + new LocTextKey("sebserver.dialog.confirm.title"); + + private final static LocTextKey DELETE_ERROR_DEPENDENCY = + new LocTextKey("sebserver.configtemplate.message.delete.partialerror"); + private final static LocTextKey DELETE_CONFIRM = + new LocTextKey("sebserver.configtemplate.message.delete.confirm"); + private final PageService pageService; private final RestService restService; private final CurrentUser currentUser; @@ -192,6 +216,20 @@ public class ConfigTemplateForm implements TemplateComposer { TemplateAttribute.FILTER_ATTR_TYPE, this.resourceService::getAttributeTypeResources); + // TODO move this to an supplier that also can be updated + // the follow-up configuration + final Configuration configuration = this.restService + .getBuilder(GetConfigurations.class) + .withQueryParam(Configuration.FILTER_ATTR_CONFIGURATION_NODE_ID, examConfig.getModelId()) + .withQueryParam(Configuration.FILTER_ATTR_FOLLOWUP, Constants.TRUE_STRING) + .call() + .map(Utils::toSingleton) + .onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION, error)) + .getOrThrow(); + final AttributeValueSupplier attributeValueSupplier = new AttributeValueSupplier( + this.pageService, + configuration.getModelId()); + final EntityTable attrTable = this.pageService.entityTableBuilder( Domain.CONFIGURATION_NODE.TYPE_NAME + "_Template", @@ -200,6 +238,7 @@ public class ConfigTemplateForm implements TemplateComposer { API.PARAM_PARENT_MODEL_ID, entityKey.modelId)) .withPaging(15) + .withColumn(new ColumnDefinition<>( Domain.CONFIGURATION_ATTRIBUTE.ATTR_NAME, ATTRIBUTES_LIST_NAME_TEXT_KEY, @@ -207,6 +246,7 @@ public class ConfigTemplateForm implements TemplateComposer { .withFilter(this.nameFilter) .sortable() .widthProportion(3)) + .withColumn(new ColumnDefinition( Domain.CONFIGURATION_ATTRIBUTE.ATTR_TYPE, ATTRIBUTES_LIST_TYPE_TEXT_KEY, @@ -214,6 +254,7 @@ public class ConfigTemplateForm implements TemplateComposer { .withFilter(typeFilter) .sortable() .widthProportion(1)) + .withColumn(new ColumnDefinition<>( Domain.ORIENTATION.ATTR_VIEW_ID, ATTRIBUTES_LIST_VIEW_TEXT_KEY, @@ -221,6 +262,7 @@ public class ConfigTemplateForm implements TemplateComposer { .withFilter(viewFilter) .sortable() .widthProportion(1)) + .withColumn(new ColumnDefinition<>( Domain.ORIENTATION.ATTR_GROUP_ID, ATTRIBUTES_LIST_GROUP_TEXT_KEY, @@ -228,6 +270,13 @@ public class ConfigTemplateForm implements TemplateComposer { .withFilter(this.groupFilter) .sortable() .widthProportion(1)) + + .withColumn(new ColumnDefinition( + Domain.CONFIGURATION_VALUE.ATTR_VALUE, + ATTRIBUTES_LIST_VALUE_TEXT_KEY, + attr -> attributeValueSupplier.getAttributeValue(attr.getConfigAttribute().id)) + .widthProportion(1)) + .withDefaultActionIf( () -> modifyGrant, () -> pageActionBuilder @@ -249,7 +298,7 @@ public class ConfigTemplateForm implements TemplateComposer { .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_EDIT) .withParentEntityKey(entityKey) .withSelect( - attrTable::getSelection, + attrTable::getMultiSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY) .publishIf(() -> modifyGrant, false) @@ -257,8 +306,8 @@ public class ConfigTemplateForm implements TemplateComposer { .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_SET_DEFAULT) .withParentEntityKey(entityKey) .withSelect( - attrTable::getSelection, - action -> this.resetToDefaults(action, attrTable), + attrTable::getMultiSelection, + action -> this.resetToDefaults(attributeValueSupplier, action, attrTable), EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY) .noEventPropagation() .publishIf(() -> modifyGrant, false) @@ -266,7 +315,7 @@ public class ConfigTemplateForm implements TemplateComposer { .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_REMOVE_VIEW) .withParentEntityKey(entityKey) .withSelect( - attrTable::getSelection, + attrTable::getMultiSelection, action -> this.removeFormView(action, attrTable), EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY) .noEventPropagation() @@ -275,7 +324,7 @@ public class ConfigTemplateForm implements TemplateComposer { .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_ATTR_LIST_ATTACH_DEFAULT_VIEW) .withParentEntityKey(entityKey) .withSelect( - attrTable::getSelection, + attrTable::getMultiSelection, action -> this.attachView(action, attrTable), EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY) .noEventPropagation() @@ -291,6 +340,12 @@ public class ConfigTemplateForm implements TemplateComposer { .withEntityKey(entityKey) .publishIf(() -> modifyGrant && isReadonly) + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_DELETE) + .withEntityKey(entityKey) + .withConfirm(() -> CONFIRM_DELETE) + .withExec(this::deleteConfiguration) + .publishIf(() -> writeGrant && isReadonly) + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_CREATE_CONFIG) .withEntityKey(entityKey) .withExec(this.sebxamConfigCreationPopup.configCreationFunction( @@ -316,6 +371,36 @@ public class ConfigTemplateForm implements TemplateComposer { } + private PageAction deleteConfiguration(final PageAction action) { + final ConfigurationNode configNode = this.restService + .getBuilder(GetExamConfigNode.class) + .withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId) + .call() + .getOrThrow(); + + final Result call = this.restService + .getBuilder(DeleteExamConfiguration.class) + .withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId) + .call(); + + final PageContext pageContext = action.pageContext(); + + final EntityProcessingReport report = call.getOrThrow(); + final String configName = configNode.toName().name; + if (report.getErrors().isEmpty()) { + pageContext.publishPageMessage(DELETE_CONFIRM_TITLE, new LocTextKey(DELETE_CONFIRM.name, configName)); + } else { + pageContext.publishPageMessage( + DELETE_CONFIRM_TITLE, + new LocTextKey(DELETE_ERROR_DEPENDENCY.name, configName, + report.getErrors().iterator().next().getErrorMessage().systemMessage)); + } + + return this.pageService.pageActionBuilder(pageContext) + .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_LIST) + .create(); + } + private String getAttributeName(final TemplateAttribute attribute) { final String name = this.i18nSupport.getText( @@ -329,12 +414,14 @@ public class ConfigTemplateForm implements TemplateComposer { } private PageAction resetToDefaults( + final AttributeValueSupplier attributeValueSupplier, final PageAction action, final EntityTable attrTable) { final PageAction resetToDefaults = this.examConfigurationService.resetToDefaults(action); // reload the list - attrTable.applyFilter(); + attributeValueSupplier.update(); + attrTable.updateCurrentPage(); return resetToDefaults; } @@ -343,8 +430,8 @@ public class ConfigTemplateForm implements TemplateComposer { final EntityTable attrTable) { final PageAction removeFormView = this.examConfigurationService.removeFromView(action); - // reload the list - attrTable.applyFilter(); + // reload the page + attrTable.updateCurrentPage(); return removeFormView; } @@ -353,9 +440,54 @@ public class ConfigTemplateForm implements TemplateComposer { final EntityTable attrTable) { final PageAction attachView = this.examConfigurationService.attachToDefaultView(action); - // reload the list - attrTable.applyFilter(); + // reload the page + attrTable.updateCurrentPage(); return attachView; } + private final class AttributeValueSupplier { + + private final PageService pageService; + private final String configurationId; + private final Map attrValueMapping = new HashMap<>(); + + public AttributeValueSupplier( + final PageService pageService, + final String configurationId) { + + this.pageService = pageService; + this.configurationId = configurationId; + update(); + } + + public String getAttributeValue(final Long attributeId) { + if (this.attrValueMapping.containsKey(attributeId)) { + return this.attrValueMapping.get(attributeId); + } else { + return Constants.EMPTY_NOTE; + } + } + + private void update() { + this.attrValueMapping.clear(); + + this.pageService.getRestService() + .getBuilder(GetConfigurationValues.class) + .withQueryParam( + ConfigurationValue.FILTER_ATTR_CONFIGURATION_ID, + this.configurationId) + .call() + .getOrElse(Collections::emptyList) + .stream() + .forEach(val -> { + if (this.attrValueMapping.containsKey(val.attributeId)) { + this.attrValueMapping.put(val.attributeId, + this.attrValueMapping.get(val.attributeId) + "," + val.value); + } else { + this.attrValueMapping.put(val.attributeId, val.value); + } + }); + } + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/ConfigTemplateList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/ConfigTemplateList.java index 37eae780..1286fa34 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/ConfigTemplateList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/ConfigTemplateList.java @@ -147,7 +147,7 @@ public class ConfigTemplateList implements TemplateComposer { .publishIf(examConfigGrant::iw) .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_VIEW_FROM_LIST) - .withSelect(templateTable::getSelection, PageAction::applySingleSelectionAsEntityKey, + .withSelect(templateTable::getMultiSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_TEMPLATE_SELECTION_TEXT_KEY) .publish(false) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigForm.java index ec70de13..be3c58b6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigForm.java @@ -9,6 +9,7 @@ package ch.ethz.seb.sebserver.gui.content.configs; import java.io.IOException; +import java.util.function.Function; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; @@ -27,6 +28,7 @@ import org.springframework.stereotype.Component; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.client.ClientCredentials; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig; @@ -44,17 +46,22 @@ import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageMessageException; import ch.ethz.seb.sebserver.gui.service.page.PageService; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +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.download.DownloadService; import ch.ethz.seb.sebserver.gui.service.remote.download.SEBClientConfigDownload; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.ActivateClientConfig; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.DeactivateClientConfig; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.DeleteClientConfig; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.GetClientConfig; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.GetClientCredentials; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.NewClientConfig; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.SaveClientConfig; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; @Lazy @Component @@ -69,6 +76,19 @@ public class SEBClientConfigForm implements TemplateComposer { new LocTextKey("sebserver.clientconfig.form.title"); private static final LocTextKey FORM_NAME_TEXT_KEY = new LocTextKey("sebserver.clientconfig.form.name"); + private static final LocTextKey FORM_UPDATE_USER_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.update.user"); + private static final LocTextKey FORM_UPDATE_TIME_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.update.time"); + + private static final LocTextKey CLIENT_CREDENTIALS_TITLE_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.credentials.title"); + private static final LocTextKey CLIENT_CREDENTIALS_INFO_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.credentials.info"); + private static final LocTextKey CLIENT_CREDENTIALS_NAME_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.credentials.name"); + private static final LocTextKey CLIENT_CREDENTIALS_SECRET_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.credentials.secret"); private static final LocTextKey FORM_DATE_TEXT_KEY = new LocTextKey("sebserver.clientconfig.form.date"); @@ -114,6 +134,11 @@ public class SEBClientConfigForm implements TemplateComposer { private static final LocTextKey FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY = new LocTextKey("sebserver.clientconfig.form.encryptSecret.confirm"); + private static final LocTextKey DELETE_CONFIRM = + new LocTextKey("sebserver.clientconfig.action.delete.confirm"); + private static final LocTextKey DELETE_SUCCESS = + new LocTextKey("sebserver.clientconfig.action.delete.success"); + private static final String DEFAULT_PING_INTERVAL = String.valueOf(1000); private static final String FALLBACK_DEFAULT_TIME = String.valueOf(30 * Constants.SECOND_IN_MILLIS); private static final String FALLBACK_DEFAULT_ATTEMPTS = String.valueOf(5); @@ -193,6 +218,18 @@ public class SEBClientConfigForm implements TemplateComposer { .withEntityKey(entityKey) .publishIf(() -> modifyGrant && isReadonly) + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_DELETE) + .withEntityKey(entityKey) + .withConfirm(() -> DELETE_CONFIRM) + .withExec(this::deleteConnectionConfig) + .publishIf(() -> modifyGrant && isReadonly) + + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_SHOW_CREDENTIALS) + .withEntityKey(entityKey) + .withExec(getClientCredentialFunction(this.pageService, this.cryptor)) + .ignoreMoveAwayFromEdit() + .publishIf(() -> modifyGrant && isReadonly) + .newAction(ActionDefinition.SEB_CLIENT_CONFIG_EXPORT) .withEntityKey(entityKey) .withExec(action -> { @@ -234,6 +271,24 @@ public class SEBClientConfigForm implements TemplateComposer { .publishIf(() -> !isReadonly); } + private PageAction deleteConnectionConfig(final PageAction pageAction) { + + this.restService.getBuilder(DeleteClientConfig.class) + .withURIVariable(API.PARAM_MODEL_ID, pageAction.getEntityKey().modelId) + .call() + .onError(error -> pageAction.pageContext().notifyUnexpectedError(error)) + .ifPresent(rep -> { + if (rep.getErrors().isEmpty()) { + pageAction.pageContext().publishInfo(DELETE_SUCCESS); + } else { + pageAction.pageContext().notifyUnexpectedError( + new RuntimeException(rep.errors.iterator().next().errorMessage.details)); + } + }); + + return pageAction; + } + private void buildForm( final SEBClientConfig clientConfig, final PageContext formContext, @@ -264,11 +319,31 @@ public class SEBClientConfigForm implements TemplateComposer { Domain.SEB_CLIENT_CONFIGURATION.ATTR_INSTITUTION_ID, String.valueOf(clientConfig.getInstitutionId())) - .addFieldIf(() -> !isNew, + .addFieldIf(() -> isReadonly, () -> FormBuilder.text( Domain.SEB_CLIENT_CONFIGURATION.ATTR_DATE, FORM_DATE_TEXT_KEY, i18nSupport.formatDisplayDateWithTimeZone(clientConfig.date)) + .readonly(true) + .withInputSpan(2) + .withEmptyCellSeparation(false)) + + .addFieldIf(() -> isReadonly, + () -> FormBuilder.text( + Domain.SEB_CLIENT_CONFIGURATION.ATTR_LAST_UPDATE_TIME, + FORM_UPDATE_TIME_TEXT_KEY, + i18nSupport.formatDisplayDateWithTimeZone(clientConfig.lastUpdateTime)) + .readonly(true) + .withLabelSpan(1) + .withInputSpan(2) + .withEmptyCellSeparation(false)) + + .addFieldIf(() -> isReadonly, + () -> FormBuilder.singleSelection( + Domain.SEB_CLIENT_CONFIGURATION.ATTR_LAST_UPDATE_USER, + FORM_UPDATE_USER_TEXT_KEY, + clientConfig.lastUpdateUser, + () -> this.pageService.getResourceService().userResources()) .readonly(true)) .addField(FormBuilder.text( @@ -503,6 +578,60 @@ public class SEBClientConfigForm implements TemplateComposer { } } + public static Function getClientCredentialFunction( + final PageService pageService, + final Cryptor cryptor) { + + final RestService restService = pageService.getResourceService().getRestService(); + return action -> { + + final ClientCredentials credentials = restService + .getBuilder(GetClientCredentials.class) + .withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId) + .call() + .getOrThrow(); + + final WidgetFactory widgetFactory = pageService.getWidgetFactory(); + final ModalInputDialog dialog = new ModalInputDialog<>( + action.pageContext().getParent().getShell(), + widgetFactory); + + dialog.setDialogWidth(720); + + dialog.open( + CLIENT_CREDENTIALS_TITLE_TEXT_KEY, + action.pageContext(), + pc -> { + + final Composite content = widgetFactory.defaultPageLayout( + pc.getParent()); + + widgetFactory.labelLocalized( + content, + CustomVariant.TEXT_H3, + CLIENT_CREDENTIALS_INFO_TEXT_KEY); + + pageService.formBuilder( + action.pageContext().copyOf(content)) + .readonly(true) + .withDefaultSpanLabel(1) + .withDefaultSpanInput(6) + + .addField(FormBuilder.text( + "ClientId", + CLIENT_CREDENTIALS_NAME_TEXT_KEY, + credentials.clientIdAsString())) + + .addField(FormBuilder.password( + "ClientSecret", + CLIENT_CREDENTIALS_SECRET_TEXT_KEY, + cryptor.decrypt(credentials.secret).getOrThrow())) + .build(); + }); + return action; + }; + } + private static final class FormHandleAnchor { FormHandle formHandle; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigList.java index 18b80186..4c170341 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBClientConfigList.java @@ -173,7 +173,8 @@ public class SEBClientConfigList implements TemplateComposer { .publishIf(clientConfigGrant::iw) .newAction(ActionDefinition.SEB_CLIENT_CONFIG_VIEW_FROM_LIST) - .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) + .withSelect(table::getMultiSelection, PageAction::applySingleSelectionAsEntityKey, + EMPTY_SELECTION_TEXT_KEY) .publish(false) .newAction(ActionDefinition.SEB_CLIENT_CONFIG_MODIFY_FROM_LIST) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigBatchResetToTemplatePopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigBatchResetToTemplatePopup.java new file mode 100644 index 00000000..b6158cfe --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigBatchResetToTemplatePopup.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2022 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.configs; + +import java.util.function.Supplier; + +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType; +import ch.ethz.seb.sebserver.gbl.model.BatchAction; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.form.FormHandle; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.AbstractBatchActionWizard; +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.push.ServerPushService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class SEBExamConfigBatchResetToTemplatePopup extends AbstractBatchActionWizard { + + private final static LocTextKey FORM_TITLE = + new LocTextKey("sebserver.examconfig.list.batch.reset.title"); + private final static LocTextKey ACTION_DO_RESET = + new LocTextKey("sebserver.examconfig.list.batch.action.reset"); + private final static LocTextKey FORM_INFO = + new LocTextKey("sebserver.examconfig.list.batch.action.reset.info"); + + protected SEBExamConfigBatchResetToTemplatePopup( + final PageService pageService, + final ServerPushService serverPushService) { + + super(pageService, serverPushService); + } + + @Override + protected LocTextKey getTitle() { + return FORM_TITLE; + } + + @Override + protected LocTextKey getBatchActionInfo() { + return FORM_INFO; + } + + @Override + protected LocTextKey getBatchActionTitle() { + return ACTION_DO_RESET; + } + + @Override + protected BatchActionType getBatchActionType() { + return BatchActionType.EXAM_CONFIG_REST_TEMPLATE_SETTINGS; + } + + @Override + protected Supplier createResultPageSupplier( + final PageContext pageContext, + final FormHandle formHandle) { + + // No specific fields for this action + return () -> pageContext; + } + + @Override + protected void extendBatchActionRequest( + final PageContext pageContext, + final RestCall.RestCallBuilder batchActionRequestBuilder) { + + // Nothing to do here + } + + @Override + protected FormBuilder buildSpecificFormFields( + final PageContext formContext, + final FormBuilder formHead, + final boolean readonly) { + + // No specific fields for this action + return formHead; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigBatchStateChangePopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigBatchStateChangePopup.java new file mode 100644 index 00000000..9842a03a --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigBatchStateChangePopup.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2022 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.configs; + +import java.util.function.Supplier; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType; +import ch.ethz.seb.sebserver.gbl.model.BatchAction; +import ch.ethz.seb.sebserver.gbl.model.Domain; +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.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.form.FormHandle; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.AbstractBatchActionWizard; +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.push.ServerPushService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class SEBExamConfigBatchStateChangePopup extends AbstractBatchActionWizard { + + private static final String ATTR_SELECTED_TARGET_STATE = "selectedTargetState"; + + private final static LocTextKey FORM_TITLE = + new LocTextKey("sebserver.examconfig.list.batch.statechange.title"); + private final static LocTextKey ACTION_DO_STATE_CHANGE = + new LocTextKey("sebserver.examconfig.list.batch.action.statechange"); + private final static LocTextKey FORM_INFO = + new LocTextKey("sebserver.examconfig.list.batch.action.statechange.info"); + private final static LocTextKey FORM_STATUS_TEXT_KEY = + new LocTextKey("sebserver.examconfig.list.batch.action.status"); + + protected SEBExamConfigBatchStateChangePopup( + final PageService pageService, + final ServerPushService serverPushService) { + + super(pageService, serverPushService); + } + + @Override + protected LocTextKey getTitle() { + return FORM_TITLE; + } + + @Override + protected LocTextKey getBatchActionInfo() { + return FORM_INFO; + } + + @Override + protected LocTextKey getBatchActionTitle() { + return ACTION_DO_STATE_CHANGE; + } + + @Override + protected BatchActionType getBatchActionType() { + return BatchActionType.EXAM_CONFIG_STATE_CHANGE; + } + + @Override + public FormBuilder buildSpecificFormFields( + final PageContext formContext, + final FormBuilder formHead, + final boolean readonly) { + + final String targetStateName = readonly + ? formContext.getAttribute(ATTR_SELECTED_TARGET_STATE) + : ConfigurationStatus.CONSTRUCTION.name(); + + return formHead.addField(FormBuilder.singleSelection( + Domain.CONFIGURATION_NODE.ATTR_STATUS, + FORM_STATUS_TEXT_KEY, + targetStateName, + () -> this.pageService.getResourceService() + .examConfigStatusResourcesAll()) + .readonly(readonly)); + } + + @Override + protected Supplier createResultPageSupplier( + final PageContext pageContext, + final FormHandle formHandle) { + + return () -> pageContext.withAttribute( + ATTR_SELECTED_TARGET_STATE, + formHandle.getForm().getFieldValue(Domain.CONFIGURATION_NODE.ATTR_STATUS)); + } + + @Override + protected void extendBatchActionRequest( + final PageContext pageContext, + final RestCall.RestCallBuilder batchActionRequestBuilder) { + + final String targetStateName = pageContext.getAttribute(ATTR_SELECTED_TARGET_STATE); + if (StringUtils.isBlank(targetStateName)) { + throw new IllegalArgumentException("missing " + ATTR_SELECTED_TARGET_STATE + " from pageContext"); + } + + batchActionRequestBuilder.withFormParam(BatchAction.ACTION_ATTRIBUT_TARGET_STATE, targetStateName); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigCreationPopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigCreationPopup.java index 78eebe6f..cc76b8dd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigCreationPopup.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigCreationPopup.java @@ -71,13 +71,11 @@ public class SEBExamConfigCreationPopup { .setLargeDialogWidth(); final CreationFormContext formContext = new CreationFormContext( - this.pageService, pageContext, copyAsTemplate, createFromTemplate); final Predicate> doCopy = formHandle -> doCreate( - this.pageService, pageContext, copyAsTemplate, createFromTemplate, @@ -100,7 +98,6 @@ public class SEBExamConfigCreationPopup { } private boolean doCreate( - final PageService pageService, final PageContext pageContext, final boolean copyAsTemplate, final boolean createFromTemplate, @@ -111,7 +108,7 @@ public class SEBExamConfigCreationPopup { ? NewExamConfig.class : CopyConfiguration.class; - final ConfigurationNode newConfig = pageService + final ConfigurationNode newConfig = this.pageService .getRestService() .getBuilder(restCall) .withFormBinding(formHandle.getFormBinding()) @@ -125,34 +122,31 @@ public class SEBExamConfigCreationPopup { // view either new template or configuration final PageAction viewCopy = (copyAsTemplate) - ? pageService.pageActionBuilder(pageContext) + ? this.pageService.pageActionBuilder(pageContext) .newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_VIEW) .withEntityKey(new EntityKey(newConfig.id, EntityType.CONFIGURATION_NODE)) .create() - : pageService.pageActionBuilder(pageContext) + : this.pageService.pageActionBuilder(pageContext) .newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP) .withEntityKey(new EntityKey(newConfig.id, EntityType.CONFIGURATION_NODE)) .create(); - pageService.executePageAction(viewCopy); + this.pageService.executePageAction(viewCopy); return true; } private final class CreationFormContext implements ModalInputDialogComposer> { - private final PageService pageService; private final PageContext pageContext; private final boolean copyAsTemplate; private final boolean createFromTemplate; protected CreationFormContext( - final PageService pageService, final PageContext pageContext, final boolean copyAsTemplate, final boolean createFromTemplate) { - this.pageService = pageService; this.pageContext = pageContext; this.copyAsTemplate = copyAsTemplate; this.createFromTemplate = createFromTemplate; @@ -161,11 +155,11 @@ public class SEBExamConfigCreationPopup { @Override public Supplier> compose(final Composite parent) { - final Composite grid = this.pageService.getWidgetFactory() + final Composite grid = SEBExamConfigCreationPopup.this.pageService.getWidgetFactory() .createPopupScrollComposite(parent); final EntityKey entityKey = this.pageContext.getEntityKey(); - final FormHandle formHandle = this.pageService.formBuilder( + final FormHandle formHandle = SEBExamConfigCreationPopup.this.pageService.formBuilder( this.pageContext.copyOf(grid)) .readonly(false) .putStaticValueIf( diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigForm.java index 6186cc93..e99c0f83 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigForm.java @@ -27,6 +27,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; +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.QuizData; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigKey; @@ -58,6 +59,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.De import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ExportConfigKey; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.NewExamConfig; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ResetToTemplateSettings; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfig; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; @@ -71,12 +73,19 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; @GuiProfile public class SEBExamConfigForm implements TemplateComposer { + private static final LocTextKey MESSAGE_RESET_TO_TEMPL_SUCCESS = + new LocTextKey("sebserver.examconfig.action.restore.template.settings.success"); static final LocTextKey FORM_TITLE_NEW = new LocTextKey("sebserver.examconfig.form.title.new"); static final LocTextKey FORM_TITLE = new LocTextKey("sebserver.examconfig.form.title"); static final LocTextKey FORM_NAME_TEXT_KEY = new LocTextKey("sebserver.examconfig.form.name"); + static final LocTextKey FORM_UPDATE_TIME_TEXT_KEY = + new LocTextKey("sebserver.examconfig.form.update.time"); + static final LocTextKey FORM_UPDATE_USER_TEXT_KEY = + new LocTextKey("sebserver.examconfig.form.update.user"); + static final LocTextKey FORM_DESCRIPTION_TEXT_KEY = new LocTextKey("sebserver.examconfig.form.description"); static final LocTextKey FORM_HISTORY_TEXT_KEY = @@ -101,6 +110,8 @@ public class SEBExamConfigForm implements TemplateComposer { new LocTextKey("sebserver.examconfig.form.attached-to"); static final LocTextKey FORM_ATTACHED_EXAMS_TITLE_TOOLTIP_TEXT_KEY = new LocTextKey("sebserver.examconfig.form.attached-to" + Constants.TOOLTIP_TEXT_KEY_SUFFIX); + static final LocTextKey FORM_RESET_CONFIRM = + new LocTextKey("sebserver.examconfig.action.restore.template.settings.confirm"); static final LocTextKey SAVE_CONFIRM_STATE_CHANGE_WHILE_ATTACHED = new LocTextKey("sebserver.examconfig.action.state-change.confirm"); @@ -168,6 +179,16 @@ public class SEBExamConfigForm implements TemplateComposer { .call() .map(names -> names != null && !names.isEmpty()) .getOr(Boolean.FALSE); + final boolean hasRunningExam = isAttachedToExam && this.restService + .getBuilder(GetExamConfigMappingsPage.class) + .withQueryParam(ExamConfigurationMap.FILTER_ATTR_CONFIG_ID, examConfig.getModelId()) + .call() + .map(res -> res.content + .stream() + .filter(map -> map.examStatus == ExamStatus.RUNNING) + .findAny() + .isPresent()) + .getOr(false); // new PageContext with actual EntityKey final PageContext formContext = pageContext.withEntityKey(examConfig.getEntityKey()); @@ -203,6 +224,28 @@ public class SEBExamConfigForm implements TemplateComposer { : String.valueOf(examConfig.templateId), resourceService::getExamConfigTemplateResources) .readonly(!isNew)) + + .addFieldIf(() -> isReadonly, + () -> FormBuilder.text( + Domain.CONFIGURATION_NODE.ATTR_LAST_UPDATE_TIME, + FORM_UPDATE_TIME_TEXT_KEY, + this.pageService.getI18nSupport() + .formatDisplayDateWithTimeZone(examConfig.lastUpdateTime)) + .readonly(true) + .withInputSpan(2) + .withEmptyCellSeparation(false)) + + .addFieldIf(() -> isReadonly, + () -> FormBuilder.singleSelection( + Domain.SEB_CLIENT_CONFIGURATION.ATTR_LAST_UPDATE_USER, + FORM_UPDATE_USER_TEXT_KEY, + examConfig.lastUpdateUser, + () -> this.pageService.getResourceService().userResources()) + .readonly(true) + .withLabelSpan(1) + .withInputSpan(2) + .withEmptyCellSeparation(false)) + .addField(FormBuilder.text( Domain.CONFIGURATION_NODE.ATTR_NAME, FORM_NAME_TEXT_KEY, @@ -218,7 +261,7 @@ public class SEBExamConfigForm implements TemplateComposer { Domain.CONFIGURATION_NODE.ATTR_STATUS, FORM_STATUS_TEXT_KEY, examConfig.status.name(), - () -> resourceService.examConfigStatusResources(isAttachedToExam)) + () -> resourceService.examConfigStatusResources(isAttachedToExam, hasRunningExam)) .withEmptyCellSeparation(!isReadonly)) .buildFor((isNew) ? this.restService.getRestCall(NewExamConfig.class) @@ -248,6 +291,17 @@ public class SEBExamConfigForm implements TemplateComposer { .withAttribute(PageContext.AttributeKeys.READ_ONLY, String.valueOf(!modifyGrant)) .publishIf(() -> isReadonly) + .newAction(ActionDefinition.SEB_EXAM_CONFIG_RESET_TO_TEMPLATE_SETTINGS) + .withConfirm(() -> FORM_RESET_CONFIRM) + .withEntityKey(entityKey) + .withExec(this::restoreToTemplateSettings) + .noEventPropagation() + .publishIf(() -> modifyGrant + && isReadonly + && examConfig.status != ConfigurationStatus.IN_USE + && examConfig.templateId != null + && examConfig.templateId.longValue() > 0) + .newAction(ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG) .withEntityKey(entityKey) .withExec(this.sebExamConfigCreationPopup.configCreationFunction( @@ -281,7 +335,7 @@ public class SEBExamConfigForm implements TemplateComposer { .withEntityKey(entityKey) .withExec(formHandle::processFormSave) .ignoreMoveAwayFromEdit() - .withConfirm(() -> stateChangeConfirm(isAttachedToExam, formHandle)) + .withConfirm(() -> stateChangeConfirm(hasRunningExam, formHandle)) .publishIf(() -> !isReadonly) .newAction(ActionDefinition.SEB_EXAM_CONFIG_PROP_CANCEL_MODIFY) @@ -341,6 +395,18 @@ public class SEBExamConfigForm implements TemplateComposer { } } + private PageAction restoreToTemplateSettings(final PageAction action) { + this.restService.getBuilder(ResetToTemplateSettings.class) + .withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId) + .call() + .getOrThrow(); + + action.pageContext().publishInfo(MESSAGE_RESET_TO_TEMPL_SUCCESS); + + return action; + + } + private PageAction deleteConfiguration(final PageAction action) { final ConfigurationNode configNode = this.restService .getBuilder(GetExamConfigNode.class) @@ -408,17 +474,17 @@ public class SEBExamConfigForm implements TemplateComposer { } private LocTextKey stateChangeConfirm( - final boolean isAttachedToExam, + final boolean hasRunningExam, final FormHandle formHandle) { - if (isAttachedToExam) { + if (hasRunningExam) { final String fieldValue = formHandle .getForm() .getFieldValue(Domain.CONFIGURATION_NODE.ATTR_STATUS); if (fieldValue != null) { final ConfigurationStatus state = ConfigurationStatus.valueOf(fieldValue); - if (state != ConfigurationStatus.IN_USE) { + if (state != ConfigurationStatus.IN_USE && state != ConfigurationStatus.ARCHIVED) { return SAVE_CONFIRM_STATE_CHANGE_WHILE_ATTACHED; } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigList.java index 96da9d56..984bc580 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/configs/SEBExamConfigList.java @@ -57,6 +57,8 @@ public class SEBExamConfigList implements TemplateComposer { new LocTextKey("sebserver.examconfig.list.column.description"); private static final LocTextKey STATUS_TEXT_KEY = new LocTextKey("sebserver.examconfig.list.column.status"); + private static final LocTextKey TEMPLATE_TEXT_KEY = + new LocTextKey("sebserver.examconfig.list.column.template"); private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = new LocTextKey("sebserver.examconfig.info.pleaseSelect"); @@ -66,10 +68,13 @@ public class SEBExamConfigList implements TemplateComposer { private final TableFilterAttribute descFilter = new TableFilterAttribute(CriteriaType.TEXT, ConfigurationNode.FILTER_ATTR_DESCRIPTION); private final TableFilterAttribute statusFilter; + private final TableFilterAttribute templateFilter; private final PageService pageService; private final SEBExamConfigImportPopup sebExamConfigImportPopup; private final SEBExamConfigCreationPopup sebExamConfigCreationPopup; + private final SEBExamConfigBatchStateChangePopup sebExamConfigBatchStateChangePopup; + private final SEBExamConfigBatchResetToTemplatePopup sebExamConfigBatchResetToTemplatePopup; private final CurrentUser currentUser; private final ResourceService resourceService; private final int pageSize; @@ -78,11 +83,15 @@ public class SEBExamConfigList implements TemplateComposer { final PageService pageService, final SEBExamConfigImportPopup sebExamConfigImportPopup, final SEBExamConfigCreationPopup sebExamConfigCreationPopup, + final SEBExamConfigBatchStateChangePopup sebExamConfigBatchStateChangePopup, + final SEBExamConfigBatchResetToTemplatePopup sebExamConfigBatchResetToTemplatePopup, @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { this.pageService = pageService; this.sebExamConfigImportPopup = sebExamConfigImportPopup; this.sebExamConfigCreationPopup = sebExamConfigCreationPopup; + this.sebExamConfigBatchStateChangePopup = sebExamConfigBatchStateChangePopup; + this.sebExamConfigBatchResetToTemplatePopup = sebExamConfigBatchResetToTemplatePopup; this.currentUser = pageService.getCurrentUser(); this.resourceService = pageService.getResourceService(); this.pageSize = pageSize; @@ -96,6 +105,11 @@ public class SEBExamConfigList implements TemplateComposer { CriteriaType.SINGLE_SELECTION, ConfigurationNode.FILTER_ATTR_STATUS, this.resourceService::examConfigStatusFilterResources); + + this.templateFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + ConfigurationNode.FILTER_ATTR_TEMPLATE_ID, + this.resourceService::getExamConfigTemplateResourcesSelection); } @Override @@ -112,6 +126,7 @@ public class SEBExamConfigList implements TemplateComposer { // exam configuration table final EntityTable configTable = this.pageService.entityTableBuilder(GetExamConfigNodePage.class) + .withMultiSelection() .withStaticFilter( Domain.CONFIGURATION_NODE.ATTR_TYPE, ConfigurationType.EXAM_CONFIG.name()) @@ -146,6 +161,13 @@ public class SEBExamConfigList implements TemplateComposer { this.resourceService::localizedExamConfigStatusName) .withFilter(this.statusFilter) .sortable()) + + .withColumn(new ColumnDefinition<>( + Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID, + TEMPLATE_TEXT_KEY, + this.resourceService.examConfigTemplateNameFunction()) + .withFilter(this.templateFilter)) + .withDefaultAction(pageActionBuilder .newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST) .create()) @@ -154,7 +176,9 @@ public class SEBExamConfigList implements TemplateComposer { pageContext, ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST, ActionDefinition.SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST, - ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG_FROM_LIST)) + ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG_FROM_LIST, + ActionDefinition.SEB_EXAM_CONFIG_BULK_STATE_CHANGE, + ActionDefinition.SEB_EXAM_CONFIG_BULK_RESET_TO_TEMPLATE)) .compose(pageContext.copyOf(content)); @@ -166,7 +190,7 @@ public class SEBExamConfigList implements TemplateComposer { .newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP_FROM_LIST) .withSelect( - configTable::getSelection, + configTable::getMultiSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) .publish(false) @@ -179,7 +203,6 @@ public class SEBExamConfigList implements TemplateComposer { .publishIf(() -> examConfigGrant.im(), false) .newAction(ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG_FROM_LIST) - //.withEntityKey(entityKey) .withSelect( configTable.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION), pageAction -> { @@ -199,6 +222,22 @@ public class SEBExamConfigList implements TemplateComposer { .noEventPropagation() .publishIf(() -> examConfigGrant.im(), false) + .newAction(ActionDefinition.SEB_EXAM_CONFIG_BULK_STATE_CHANGE) + .withSelect( + configTable::getMultiSelection, + this.sebExamConfigBatchStateChangePopup.popupCreationFunction(pageContext), + EMPTY_SELECTION_TEXT_KEY) + .noEventPropagation() + .publishIf(() -> examConfigGrant.im(), false) + + .newAction(ActionDefinition.SEB_EXAM_CONFIG_BULK_RESET_TO_TEMPLATE) + .withSelect( + configTable::getMultiSelection, + this.sebExamConfigBatchResetToTemplatePopup.popupCreationFunction(pageContext), + EMPTY_SELECTION_TEXT_KEY) + .noEventPropagation() + .publishIf(() -> examConfigGrant.im(), false) + .newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_TO_NEW_CONFIG) .withSelect( configTable.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java index ae492339..33fc6c3f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java @@ -61,12 +61,13 @@ import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.ArchiveExam; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckExamConsistency; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckSEBRestriction; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetDefaultExamTemplate; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplate; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.TestLmsSetup; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizData; @@ -118,6 +119,8 @@ public class ExamForm implements TemplateComposer { new LocTextKey("sebserver.exam.form.examTemplate"); private static final LocTextKey FORM_EXAM_TEMPLATE_ERROR = new LocTextKey("sebserver.exam.form.examTemplate.error"); + private static final LocTextKey EXAM_ARCHIVE_CONFIRM = + new LocTextKey("sebserver.exam.action.archive.confirm"); private final static LocTextKey CONSISTENCY_MESSAGE_TITLE = new LocTextKey("sebserver.exam.consistency.title"); @@ -143,7 +146,7 @@ public class ExamForm implements TemplateComposer { private final PageService pageService; private final ResourceService resourceService; private final ExamSEBRestrictionSettings examSEBRestrictionSettings; - private final ExamProctoringSettings examProctoringSettings; + private final ProctoringSettingsPopup proctoringSettingsPopup; private final WidgetFactory widgetFactory; private final RestService restService; private final ExamDeletePopup examDeletePopup; @@ -154,7 +157,7 @@ public class ExamForm implements TemplateComposer { protected ExamForm( final PageService pageService, final ExamSEBRestrictionSettings examSEBRestrictionSettings, - final ExamProctoringSettings examProctoringSettings, + final ProctoringSettingsPopup proctoringSettingsPopup, final ExamToConfigBindingForm examToConfigBindingForm, final DownloadService downloadService, final ExamDeletePopup examDeletePopup, @@ -165,7 +168,7 @@ public class ExamForm implements TemplateComposer { this.pageService = pageService; this.resourceService = pageService.getResourceService(); this.examSEBRestrictionSettings = examSEBRestrictionSettings; - this.examProctoringSettings = examProctoringSettings; + this.proctoringSettingsPopup = proctoringSettingsPopup; this.widgetFactory = pageService.getWidgetFactory(); this.restService = this.resourceService.getRestService(); this.examDeletePopup = examDeletePopup; @@ -240,14 +243,13 @@ public class ExamForm implements TemplateComposer { final BooleanSupplier isNew = () -> importFromQuizData; final BooleanSupplier isNotNew = () -> !isNew.getAsBoolean(); - final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(exam); - final boolean modifyGrant = userGrantCheck.m(); - final boolean writeGrant = userGrantCheck.w(); + final EntityGrantCheck entityGrantCheck = currentUser.entityGrantCheck(exam); + final boolean modifyGrant = entityGrantCheck.m(); + final boolean writeGrant = entityGrantCheck.w(); final ExamStatus examStatus = exam.getStatus(); - final boolean editable = modifyGrant && (examStatus == ExamStatus.UP_COMING || - examStatus == ExamStatus.RUNNING); + final boolean editable = modifyGrant && + (examStatus == ExamStatus.UP_COMING || examStatus == ExamStatus.RUNNING); -// TODO this is not performat try to improve by doing one check with the CheckExamConsistency above final boolean sebRestrictionAvailable = testSEBRestrictionAPI(exam); final boolean isRestricted = readonly && sebRestrictionAvailable && this.restService .getBuilder(CheckSEBRestriction.class) @@ -317,7 +319,7 @@ public class ExamForm implements TemplateComposer { .addField(FormBuilder.text( QuizData.QUIZ_ATTR_START_URL, FORM_QUIZ_URL_TEXT_KEY, - exam.startURL) + exam.getStartURL()) .readonly(true) .withInputSpan(7) .withEmptyCellSeparation(false)) @@ -325,7 +327,7 @@ public class ExamForm implements TemplateComposer { .addField(FormBuilder.text( QuizData.QUIZ_ATTR_DESCRIPTION, FORM_DESCRIPTION_TEXT_KEY, - exam.description) + exam.getDescription()) .asHTML(50) .readonly(true) .withInputSpan(6) @@ -391,7 +393,7 @@ public class ExamForm implements TemplateComposer { } final boolean proctoringEnabled = importFromQuizData ? false : this.restService - .getBuilder(GetProctoringSettings.class) + .getBuilder(GetExamProctoringSettings.class) .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) .call() .map(ProctoringServiceSettings::getEnableProctoring) @@ -408,6 +410,17 @@ public class ExamForm implements TemplateComposer { .withEntityKey(entityKey) .publishIf(() -> modifyGrant && readonly && editable) + .newAction(ActionDefinition.EXAM_DELETE) + .withEntityKey(entityKey) + .withExec(this.examDeletePopup.deleteWizardFunction(pageContext)) + .publishIf(() -> writeGrant && readonly) + + .newAction(ActionDefinition.EXAM_ARCHIVE) + .withEntityKey(entityKey) + .withConfirm(() -> EXAM_ARCHIVE_CONFIRM) + .withExec(this::archiveExam) + .publishIf(() -> writeGrant && readonly && examStatus == ExamStatus.FINISHED) + .newAction(ActionDefinition.EXAM_SAVE) .withExec(action -> (importFromQuizData) ? importExam(action, formHandle, sebRestrictionAvailable && exam.status == ExamStatus.RUNNING) @@ -432,7 +445,7 @@ public class ExamForm implements TemplateComposer { .withEntityKey(entityKey) .withExec(this.examSEBRestrictionSettings.settingsFunction(this.pageService)) .withAttribute(ExamSEBRestrictionSettings.PAGE_CONTEXT_ATTR_LMS_ID, String.valueOf(exam.lmsSetupId)) - .withAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY, String.valueOf(!modifyGrant)) + .withAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY, String.valueOf(!modifyGrant || !editable)) .noEventPropagation() .publishIf(() -> sebRestrictionAvailable && readonly) @@ -451,20 +464,15 @@ public class ExamForm implements TemplateComposer { .newAction(ActionDefinition.EXAM_PROCTORING_ON) .withEntityKey(entityKey) - .withExec(this.examProctoringSettings.settingsFunction(this.pageService, modifyGrant)) + .withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable)) .noEventPropagation() - .publishIf(() -> editable && proctoringEnabled && readonly) + .publishIf(() -> proctoringEnabled && readonly) .newAction(ActionDefinition.EXAM_PROCTORING_OFF) .withEntityKey(entityKey) - .withExec(this.examProctoringSettings.settingsFunction(this.pageService, modifyGrant)) + .withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable)) .noEventPropagation() - .publishIf(() -> editable && !proctoringEnabled && readonly) - - .newAction(ActionDefinition.EXAM_DELETE) - .withEntityKey(entityKey) - .withExec(this.examDeletePopup.deleteWizardFunction(pageContext)) - .publishIf(() -> writeGrant && readonly); + .publishIf(() -> !proctoringEnabled && readonly); // additional data in read-only view if (readonly && !importFromQuizData) { @@ -472,7 +480,7 @@ public class ExamForm implements TemplateComposer { this.examFormConfigs.compose( formContext .copyOf(content) - .withAttribute(ATTR_READ_GRANT, String.valueOf(userGrantCheck.r())) + .withAttribute(ATTR_READ_GRANT, String.valueOf(entityGrantCheck.r())) .withAttribute(ATTR_EDITABLE, String.valueOf(editable)) .withAttribute(ATTR_EXAM_STATUS, examStatus.name())); @@ -480,12 +488,22 @@ public class ExamForm implements TemplateComposer { this.examFormIndicators.compose( formContext .copyOf(content) - .withAttribute(ATTR_READ_GRANT, String.valueOf(userGrantCheck.r())) + .withAttribute(ATTR_READ_GRANT, String.valueOf(entityGrantCheck.r())) .withAttribute(ATTR_EDITABLE, String.valueOf(editable)) .withAttribute(ATTR_EXAM_STATUS, examStatus.name())); } } + private PageAction archiveExam(final PageAction action) { + + this.restService.getBuilder(ArchiveExam.class) + .withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId) + .call() + .onError(error -> action.pageContext().notifyUnexpectedError(error)); + + return action; + } + private String getDefaultExamTemplateId() { return this.restService.getBuilder(GetDefaultExamTemplate.class) .call() @@ -578,7 +596,7 @@ public class ExamForm implements TemplateComposer { } private boolean testSEBRestrictionAPI(final Exam exam) { - if (exam.status == ExamStatus.CORRUPT_NO_LMS_CONNECTION || exam.status == ExamStatus.CORRUPT_INVALID_ID) { + if (!exam.isLmsAvailable() || exam.status == ExamStatus.ARCHIVED) { return false; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamFormIndicators.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamFormIndicators.java index 650be970..34915995 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamFormIndicators.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamFormIndicators.java @@ -133,7 +133,7 @@ public class ExamFormIndicators implements TemplateComposer { .newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST) .withParentEntityKey(entityKey) .withSelect( - indicatorTable::getSelection, + indicatorTable::getMultiSelection, PageAction::applySingleSelectionAsEntityKey, INDICATOR_EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> editable && indicatorTable.hasAnyContent(), false) @@ -141,7 +141,7 @@ public class ExamFormIndicators implements TemplateComposer { .newAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST) .withEntityKey(entityKey) .withSelect( - indicatorTable::getSelection, + indicatorTable::getMultiSelection, this::deleteSelectedIndicator, INDICATOR_EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> editable && indicatorTable.hasAnyContent(), false) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamList.java index 906446db..e36488bf 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamList.java @@ -12,6 +12,7 @@ import java.util.function.BiConsumer; import java.util.function.BooleanSupplier; import java.util.function.Function; +import org.apache.commons.lang3.BooleanUtils; import org.eclipse.rap.rwt.RWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.TableItem; @@ -29,7 +30,6 @@ import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; 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.QuizData; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.user.UserRole; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; @@ -76,6 +76,8 @@ public class ExamList implements TemplateComposer { new LocTextKey("sebserver.exam.list.column.lmssetup"); public final static LocTextKey COLUMN_TITLE_NAME_KEY = new LocTextKey("sebserver.exam.list.column.name"); + public final static LocTextKey COLUMN_TITLE_STATE_KEY = + new LocTextKey("sebserver.exam.list.column.state"); public final static LocTextKey COLUMN_TITLE_TYPE_KEY = new LocTextKey("sebserver.exam.list.column.type"); public final static LocTextKey NO_MODIFY_OF_OUT_DATED_EXAMS = @@ -86,7 +88,8 @@ public class ExamList implements TemplateComposer { private final TableFilterAttribute institutionFilter; private final TableFilterAttribute lmsFilter; private final TableFilterAttribute nameFilter = - new TableFilterAttribute(CriteriaType.TEXT, QuizData.FILTER_ATTR_NAME); + new TableFilterAttribute(CriteriaType.TEXT, Domain.EXAM.ATTR_QUIZ_NAME); + private final TableFilterAttribute stateFilter; private final TableFilterAttribute typeFilter; private final PageService pageService; @@ -111,6 +114,11 @@ public class ExamList implements TemplateComposer { LmsSetup.FILTER_ATTR_LMS_SETUP, this.resourceService::lmsSetupResource); + this.stateFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Exam.FILTER_ATTR_STATUS, + this.resourceService::localizedExamStatusSelection); + this.typeFilter = new TableFilterAttribute( CriteriaType.SINGLE_SELECTION, Exam.FILTER_ATTR_TYPE, @@ -168,26 +176,33 @@ public class ExamList implements TemplateComposer { .sortable()) .withColumn(new ColumnDefinition<>( - QuizData.QUIZ_ATTR_NAME, + Domain.EXAM.ATTR_QUIZ_NAME, COLUMN_TITLE_NAME_KEY, Exam::getName) .withFilter(this.nameFilter) .sortable()) .withColumn(new ColumnDefinition<>( - QuizData.QUIZ_ATTR_START_TIME, + Domain.EXAM.ATTR_QUIZ_START_TIME, new LocTextKey( EXAM_LIST_COLUMN_START_TIME, i18nSupport.getUsersTimeZoneTitleSuffix()), Exam::getStartTime) .withFilter(new TableFilterAttribute( CriteriaType.DATE, - QuizData.FILTER_ATTR_START_TIME, + Domain.EXAM.ATTR_QUIZ_START_TIME, Utils.toDateTimeUTC(Utils.getMillisecondsNow()) .minusYears(1) .toString())) .sortable()) + .withColumn(new ColumnDefinition<>( + Domain.EXAM.ATTR_STATUS, + COLUMN_TITLE_STATE_KEY, + this.resourceService::localizedExamStatusName) + .withFilter(this.stateFilter) + .sortable()) + .withColumn(new ColumnDefinition( Domain.EXAM.ATTR_TYPE, COLUMN_TITLE_TYPE_KEY, @@ -210,7 +225,8 @@ public class ExamList implements TemplateComposer { final GrantCheck userGrant = currentUser.grantCheck(EntityType.EXAM); actionBuilder .newAction(ActionDefinition.EXAM_VIEW_FROM_LIST) - .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) + .withSelect(table::getMultiSelection, PageAction::applySingleSelectionAsEntityKey, + EMPTY_SELECTION_TEXT_KEY) .publish(false) .newAction(ActionDefinition.EXAM_MODIFY_FROM_LIST) @@ -257,7 +273,12 @@ public class ExamList implements TemplateComposer { final Exam exam, final PageService pageService) { - if (exam.getStatus() == ExamStatus.UP_COMING || exam.getStatus() == ExamStatus.FINISHED) { + if (BooleanUtils.isFalse(exam.isLmsAvailable())) { + item.setData(RWT.CUSTOM_VARIANT, CustomVariant.DISABLED.key); + return; + } + + if (exam.getStatus() != ExamStatus.RUNNING) { return; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateForm.java index a047df9d..c8980166 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateForm.java @@ -23,6 +23,7 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate; import ch.ethz.seb.sebserver.gbl.model.exam.IndicatorTemplate; +import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; @@ -39,11 +40,12 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteExamTemplate; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteIndicatorTemplate; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplate; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplateProctoringSettings; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorTemplatePage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewExamTemplate; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamTemplate; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.GrantCheck; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; import ch.ethz.seb.sebserver.gui.table.EntityTable; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @@ -93,13 +95,17 @@ public class ExamTemplateForm implements TemplateComposer { private final ResourceService resourceService; private final WidgetFactory widgetFactory; private final RestService restService; + private final ProctoringSettingsPopup proctoringSettingsPopup; - public ExamTemplateForm(final PageService pageService) { + public ExamTemplateForm( + final PageService pageService, + final ProctoringSettingsPopup proctoringSettingsPopup) { this.pageService = pageService; this.resourceService = pageService.getResourceService(); this.widgetFactory = pageService.getWidgetFactory(); this.restService = pageService.getRestService(); + this.proctoringSettingsPopup = proctoringSettingsPopup; } @Override @@ -185,13 +191,20 @@ public class ExamTemplateForm implements TemplateComposer { ? this.restService.getRestCall(NewExamTemplate.class) : this.restService.getRestCall(SaveExamTemplate.class)); - final GrantCheck userGrant = currentUser.grantCheck(EntityType.EXAM_TEMPLATE); + final boolean proctoringEnabled = !isNew && this.restService + .getBuilder(GetExamTemplateProctoringSettings.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .map(ProctoringServiceSettings::getEnableProctoring) + .getOr(false); + + final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(examTemplate); // propagate content actions to action-pane this.pageService.pageActionBuilder(formContext.clearEntityKeys()) .newAction(ActionDefinition.EXAM_TEMPLATE_MODIFY) .withEntityKey(entityKey) - .publishIf(() -> userGrant.im() && readonly) + .publishIf(() -> userGrantCheck.m() && readonly) .newAction(ActionDefinition.EXAM_TEMPLATE_SAVE) .withEntityKey(entityKey) @@ -208,9 +221,19 @@ public class ExamTemplateForm implements TemplateComposer { .withEntityKey(entityKey) .withConfirm(() -> EXAM_TEMPLATE_DELETE_CONFIRM) .withExec(this::deleteExamTemplate) - .publishIf(() -> userGrant.iw() && readonly) + .publishIf(() -> userGrantCheck.w() && readonly) - ; + .newAction(ActionDefinition.EXAM_TEMPLATE_PROCTORING_ON) + .withEntityKey(entityKey) + .withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, userGrantCheck.m())) + .noEventPropagation() + .publishIf(() -> proctoringEnabled && readonly) + + .newAction(ActionDefinition.EXAM_TEMPLATE_PROCTORING_OFF) + .withEntityKey(entityKey) + .withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, userGrantCheck.m())) + .noEventPropagation() + .publishIf(() -> !proctoringEnabled && readonly); if (readonly) { @@ -250,7 +273,7 @@ public class ExamTemplateForm implements TemplateComposer { .asMarkup() .widthProportion(4)) .withDefaultActionIf( - () -> userGrant.im(), + () -> userGrantCheck.m(), () -> actionBuilder .newAction(ActionDefinition.INDICATOR_TEMPLATE_MODIFY_FROM_LIST) .withParentEntityKey(entityKey) @@ -268,22 +291,22 @@ public class ExamTemplateForm implements TemplateComposer { .newAction(ActionDefinition.INDICATOR_TEMPLATE_MODIFY_FROM_LIST) .withParentEntityKey(entityKey) .withSelect( - indicatorTable::getSelection, + indicatorTable::getMultiSelection, PageAction::applySingleSelectionAsEntityKey, INDICATOR_EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> userGrant.im() && indicatorTable.hasAnyContent(), false) + .publishIf(() -> userGrantCheck.m() && indicatorTable.hasAnyContent(), false) .newAction(ActionDefinition.INDICATOR_TEMPLATE_DELETE_FROM_LIST) .withEntityKey(entityKey) .withSelect( - indicatorTable::getSelection, + indicatorTable::getMultiSelection, this::deleteSelectedIndicator, INDICATOR_EMPTY_SELECTION_TEXT_KEY) - .publishIf(() -> userGrant.im() && indicatorTable.hasAnyContent(), false) + .publishIf(() -> userGrantCheck.m() && indicatorTable.hasAnyContent(), false) .newAction(ActionDefinition.INDICATOR_TEMPLATE_NEW) .withParentEntityKey(entityKey) - .publishIf(() -> userGrant.im()); + .publishIf(() -> userGrantCheck.m()); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateList.java index 760526b2..a4151cf2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamTemplateList.java @@ -175,11 +175,13 @@ public class ExamTemplateList implements TemplateComposer { .publishIf(userGrant::iw) .newAction(ActionDefinition.EXAM_TEMPLATE_VIEW_FROM_LIST) - .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) + .withSelect(table::getMultiSelection, PageAction::applySingleSelectionAsEntityKey, + EMPTY_SELECTION_TEXT_KEY) .publish(false) .newAction(ActionDefinition.EXAM_TEMPLATE_MODIFY_FROM_LIST) - .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) + .withSelect(table::getMultiSelection, PageAction::applySingleSelectionAsEntityKey, + EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> userGrant.im(), false); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupDeletePopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupDeletePopup.java new file mode 100644 index 00000000..a9eb7177 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupDeletePopup.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2020 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.exam; + +import java.util.List; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage; +import ch.ethz.seb.sebserver.gbl.model.EntityDependency; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageMessageException; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; +import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputWizard; +import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputWizard.WizardAction; +import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputWizard.WizardPage; +import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.DeleteLmsSetup; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetup; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetupDependencies; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; + +@Lazy +@Component +@GuiProfile +public class LmsSetupDeletePopup { + + private static final Logger log = LoggerFactory.getLogger(LmsSetupDeletePopup.class); + + private final static LocTextKey FORM_TITLE = + new LocTextKey("sebserver.lmssetup.delete.form.title"); + private final static LocTextKey FORM_INFO = + new LocTextKey("sebserver.lmssetup.delete.form.info"); + private final static LocTextKey FORM_REPORT_INFO = + new LocTextKey("sebserver.lmssetup.delete.report.info"); + private final static LocTextKey FORM_REPORT_LIST_TYPE = + new LocTextKey("sebserver.lmssetup.delete.report.list.type"); + private final static LocTextKey FORM_REPORT_LIST_NAME = + new LocTextKey("sebserver.lmssetup.delete.report.list.name"); + private final static LocTextKey FORM_REPORT_LIST_DESC = + new LocTextKey("sebserver.lmssetup.delete.report.list.description"); + private final static LocTextKey FORM_REPORT_NONE = + new LocTextKey("sebserver.lmssetup.delete.report.list.empty"); + + private final static LocTextKey ACTION_DELETE = + new LocTextKey("sebserver.lmssetup.delete.action.delete"); + + private final static LocTextKey DELETE_CONFIRM_TITLE = + new LocTextKey("sebserver.lmssetup.delete.confirm.title"); + private final static LocTextKey DELETE_ERROR_CONSISTENCY = + new LocTextKey("sebserver.lmssetup.action.delete.consistency.error"); + + private final PageService pageService; + + protected LmsSetupDeletePopup(final PageService pageService) { + this.pageService = pageService; + } + + public Function deleteWizardFunction(final PageContext pageContext) { + return action -> { + + final ModalInputWizard wizard = + new ModalInputWizard( + action.pageContext().getParent().getShell(), + this.pageService.getWidgetFactory()) + .setVeryLargeDialogWidth(); + + final String page1Id = "DELETE_PAGE"; + final Predicate callback = pc -> doDelete(this.pageService, pc); + final BiFunction> composePage1 = + (prefPageContext, content) -> composeDeleteDialog(content, + (prefPageContext != null) ? prefPageContext : pageContext); + + final WizardPage page1 = new WizardPage<>( + page1Id, + true, + composePage1, + new WizardAction<>(ACTION_DELETE, callback)); + + wizard.open(FORM_TITLE, Utils.EMPTY_EXECUTION, page1); + + return action; + }; + } + + private boolean doDelete( + final PageService pageService, + final PageContext pageContext) { + + try { + final EntityKey entityKey = pageContext.getEntityKey(); + final LmsSetup toDelete = this.pageService + .getRestService() + .getBuilder(GetLmsSetup.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .getOrThrow(); + + final RestCall.RestCallBuilder restCallBuilder = this.pageService.getRestService() + .getBuilder(DeleteLmsSetup.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name()); + + final Result deleteCall = restCallBuilder.call(); + if (deleteCall.hasError()) { + final Exception error = deleteCall.getError(); + if (error instanceof RestCallError) { + final APIMessage message = ((RestCallError) error) + .getAPIMessages() + .stream() + .findFirst() + .orElse(null); + if (message != null && ErrorMessage.INTEGRITY_VALIDATION.isOf(message)) { + pageContext.publishPageMessage(new PageMessageException(DELETE_ERROR_CONSISTENCY)); + return false; + } + } + } + + final EntityProcessingReport report = deleteCall.getOrThrow(); + + final PageAction action = this.pageService.pageActionBuilder(pageContext) + .newAction(ActionDefinition.LMS_SETUP_VIEW_LIST) + .create(); + + this.pageService.firePageEvent( + new ActionEvent(action), + action.pageContext()); + + final List dependencies = report.results.stream() + .filter(key -> !key.equals(entityKey)) + .collect(Collectors.toList()); + pageContext.publishPageMessage( + DELETE_CONFIRM_TITLE, + new LocTextKey( + "sebserver.lmssetup.delete.confirm.message", + toDelete.toName().name, + dependencies.size(), + (report.errors.isEmpty()) ? "no" : String.valueOf((report.errors.size())))); + return true; + } catch (final Exception e) { + log.error("Unexpected error while trying to delete LMS Setup:", e); + pageContext.notifyUnexpectedError(e); + return false; + } + } + + private Supplier composeDeleteDialog( + final Composite parent, + final PageContext pageContext) { + + final I18nSupport i18nSupport = this.pageService.getI18nSupport(); + final Composite grid = this.pageService.getWidgetFactory() + .createPopupScrollComposite(parent); + + final Label title = this.pageService.getWidgetFactory() + .labelLocalized(grid, CustomVariant.TEXT_H3, FORM_INFO); + final GridData gridData = new GridData(); + gridData.horizontalIndent = 10; + gridData.verticalIndent = 10; + title.setLayoutData(gridData); + + final Label titleReport = this.pageService.getWidgetFactory() + .labelLocalized(grid, CustomVariant.TEXT_H3, FORM_REPORT_INFO); + final GridData gridDataReport = new GridData(); + gridDataReport.horizontalIndent = 10; + gridDataReport.verticalIndent = 10; + titleReport.setLayoutData(gridDataReport); + + try { + + // get dependencies + final EntityKey entityKey = pageContext.getEntityKey(); + final RestCall>.RestCallBuilder restCallBuilder = this.pageService.getRestService() + .getBuilder(GetLmsSetupDependencies.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.HARD_DELETE.name()); + + final Set dependencies = restCallBuilder + .call() + .getOrThrow(); + final List list = dependencies + .stream() + .sorted() + .collect(Collectors.toList()); + + this.pageService. staticListTableBuilder(list, null) + .withEmptyMessage(FORM_REPORT_NONE) + .withColumn(new ColumnDefinition<>( + "FORM_REPORT_LIST_TYPE", + FORM_REPORT_LIST_TYPE, + dep -> i18nSupport + .getText("sebserver.overall.types.entityType." + dep.self.entityType.name()))) + .withColumn(new ColumnDefinition<>( + "FORM_REPORT_LIST_NAME", + FORM_REPORT_LIST_NAME, + dep -> dep.name)) + .withColumn(new ColumnDefinition( + "FORM_REPORT_LIST_DESC", + FORM_REPORT_LIST_DESC, + dep -> dep.description)) + .compose(pageContext.copyOf(grid)); + + return () -> pageContext; + } catch (final Exception e) { + log.error("Error while trying to compose LMS Setup delete report page: ", e); + pageContext.notifyUnexpectedError(e); + throw e; + } + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java index 3691cb72..8b7325aa 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupForm.java @@ -105,11 +105,15 @@ public class LmsSetupForm implements TemplateComposer { private final PageService pageService; private final ResourceService resourceService; + private final LmsSetupDeletePopup lmsSetupDeletePopup; - protected LmsSetupForm(final PageService pageService) { + protected LmsSetupForm( + final PageService pageService, + final LmsSetupDeletePopup lmsSetupDeletePopup) { this.pageService = pageService; this.resourceService = pageService.getResourceService(); + this.lmsSetupDeletePopup = lmsSetupDeletePopup; } @Override @@ -305,6 +309,11 @@ public class LmsSetupForm implements TemplateComposer { .withEntityKey(entityKey) .publishIf(() -> modifyGrant && readonly && institutionActive) + .newAction(ActionDefinition.LMS_SETUP_DELETE) + .withEntityKey(entityKey) + .withExec(this.lmsSetupDeletePopup.deleteWizardFunction(pageContext)) + .publishIf(() -> writeGrant && readonly) + .newAction(ActionDefinition.LMS_SETUP_TEST) .withEntityKey(entityKey) .withExec(action -> LmsSetupForm.testLmsSetup(action, formHandle, restService)) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupList.java index 02d3ddd7..0f3c0742 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/LmsSetupList.java @@ -169,7 +169,10 @@ public class LmsSetupList implements TemplateComposer { .publishIf(userGrant::iw) .newAction(ActionDefinition.LMS_SETUP_VIEW_FROM_LIST) - .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) + .withSelect( + table::getMultiSelection, + PageAction::applySingleSelectionAsEntityKey, + EMPTY_SELECTION_TEXT_KEY) .publish(false) .newAction(ActionDefinition.LMS_SETUP_MODIFY_FROM_LIST) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamProctoringSettings.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ProctoringSettingsPopup.java similarity index 88% rename from src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamProctoringSettings.java rename to src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ProctoringSettingsPopup.java index 2eef3c2b..00c4a11d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamProctoringSettings.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ProctoringSettingsPopup.java @@ -25,11 +25,13 @@ import org.springframework.stereotype.Component; import ch.ethz.seb.sebserver.gbl.Constants; 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.model.exam.ProctoringServiceSettings; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.form.Form; @@ -44,15 +46,17 @@ import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; 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.RestService; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveProctoringSettings; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplateProctoringSettings; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamProctoringSettings; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamTemplateProctoringSettings; @Lazy @Component @GuiProfile -public class ExamProctoringSettings { +public class ProctoringSettingsPopup { - private static final Logger log = LoggerFactory.getLogger(ExamProctoringSettings.class); + private static final Logger log = LoggerFactory.getLogger(ProctoringSettingsPopup.class); private final static LocTextKey SEB_PROCTORING_FORM_TITLE = new LocTextKey("sebserver.exam.proctoring.form.title"); @@ -90,12 +94,13 @@ public class ExamProctoringSettings { .withAttribute( PageContext.AttributeKeys.FORCE_READ_ONLY, (modifyGrant) ? Constants.FALSE_STRING : Constants.TRUE_STRING); + final ModalInputDialog> dialog = new ModalInputDialog>( action.pageContext().getParent().getShell(), pageService.getWidgetFactory()) - .setDialogWidth(740) - .setDialogHeight(400); + .setDialogWidth(860) + .setDialogHeight(600); final SEBProctoringPropertiesForm bindFormContext = new SEBProctoringPropertiesForm( pageService, @@ -176,18 +181,26 @@ public class ExamProctoringSettings { return false; } - final boolean saveOk = !pageService + final Result settings = pageService .getRestService() - .getBuilder(SaveProctoringSettings.class) + .getBuilder( + entityKey.entityType == EntityType.EXAM + ? SaveExamProctoringSettings.class + : SaveExamTemplateProctoringSettings.class) .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) .withBody(examProctoring) - .call() + .call(); + + final boolean saveOk = !settings .onError(formHandle::handleError) .hasError(); if (saveOk) { final PageAction action = pageService.pageActionBuilder(pageContext) - .newAction(ActionDefinition.EXAM_VIEW_FROM_LIST) + .newAction( + entityKey.entityType == EntityType.EXAM + ? ActionDefinition.EXAM_VIEW_FROM_LIST + : ActionDefinition.EXAM_TEMPLATE_VIEW_FROM_LIST) .create(); pageService.firePageEvent( @@ -227,7 +240,10 @@ public class ExamProctoringSettings { .createPopupScrollComposite(parent); final ProctoringServiceSettings proctoringSettings = restService - .getBuilder(GetProctoringSettings.class) + .getBuilder( + entityKey.entityType == EntityType.EXAM + ? GetExamProctoringSettings.class + : GetExamTemplateProctoringSettings.class) .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) .call() .getOrThrow(); @@ -329,24 +345,6 @@ public class ExamProctoringSettings { return () -> formHandle; } - -// TODO -// private void procServiceSelection(final Form form) { -// final ProctoringServerType proctoringServerType = ProctoringServerType -// .valueOf(form.getFieldValue(ProctoringServiceSettings.ATTR_SERVER_TYPE)); -// switch (proctoringServerType) { -// case ZOOM: { -// form.setFieldVisible(true, ProctoringServiceSettings.ATTR_SDK_KEY); -// form.setFieldVisible(true, ProctoringServiceSettings.ATTR_SDK_SECRET); -// break; -// } -// default: { -// form.setFieldVisible(false, ProctoringServiceSettings.ATTR_SDK_KEY); -// form.setFieldVisible(false, ProctoringServiceSettings.ATTR_SDK_SECRET); -// } -// } -// } - } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java index 5033ea0d..abcb58d7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/QuizLookupList.java @@ -236,7 +236,7 @@ public class QuizLookupList implements TemplateComposer { actionBuilder .newAction(ActionDefinition.QUIZ_DISCOVERY_SHOW_DETAILS) .withSelect( - table::getSelection, + table::getMultiSelection, action -> this.showDetails( action, table.getSingleSelectedROWData(), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExam.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExam.java new file mode 100644 index 00000000..5abc050a --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExam.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2022 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.monitoring; + +import java.util.Collection; +import java.util.function.BooleanSupplier; + +import org.eclipse.swt.widgets.Composite; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.Constants; +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.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; +import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; +import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; +import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; +import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; +import ch.ethz.seb.sebserver.gui.service.push.ServerPushService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamClientConnectionPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute; +import ch.ethz.seb.sebserver.gui.table.EntityTable; +import ch.ethz.seb.sebserver.gui.table.TableBuilder; +import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType; + +@Lazy +@Component +@GuiProfile +public class FinishedExam implements TemplateComposer { + + private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.finished.exam.connection.emptySelection"); + private static final LocTextKey TITLE_TEXT_KEY = + new LocTextKey("sebserver.finished.exam.connections.title"); + private static final LocTextKey EMPTY_LIST_TEXT_KEY = + new LocTextKey("sebserver.finished.exam.connections.empty"); + private static final LocTextKey TABLE_COLUMN_NAME = + new LocTextKey("sebserver.finished.exam.connections.name"); + private static final LocTextKey TABLE_COLUMN_INFO = + new LocTextKey("sebserver.finished.exam.connections.info"); + private static final LocTextKey TABLE_COLUMN_STATUS = + new LocTextKey("sebserver.finished.exam.connections.status"); + + private final TableFilterAttribute nameFilter = + new TableFilterAttribute(CriteriaType.TEXT, ClientConnection.FILTER_ATTR_SESSION_ID); + private final TableFilterAttribute infoFilter = + new TableFilterAttribute(CriteriaType.TEXT, ClientConnection.ATTR_INFO); + private final TableFilterAttribute statusFilter; + + private final PageService pageService; + private final RestService restService; + private final int pageSize; + + public FinishedExam( + final ServerPushService serverPushService, + final PageService pageService, + @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { + + this.pageService = pageService; + this.restService = pageService.getRestService(); + this.pageSize = pageSize; + + this.statusFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + ClientConnection.FILTER_ATTR_STATUS, + pageService.getResourceService()::localizedClientConnectionStatusResources); + } + + @Override + public void compose(final PageContext pageContext) { + final EntityKey examKey = pageContext.getEntityKey(); + final CurrentUser currentUser = this.pageService.getResourceService().getCurrentUser(); + final UserInfo user = currentUser.get(); + + final RestService restService = this.pageService + .getRestService(); + final PageActionBuilder actionBuilder = this.pageService + .pageActionBuilder(pageContext.clearEntityKeys()); + final Collection indicators = restService + .getBuilder(GetIndicators.class) + .withQueryParam(Indicator.FILTER_ATTR_EXAM_ID, examKey.modelId) + .call() + .getOrThrow(); + final Exam exam = this.restService.getBuilder(GetExam.class) + .withURIVariable(API.PARAM_MODEL_ID, examKey.modelId) + .call() + .getOrThrow(); + final boolean supporting = user.hasRole(UserRole.EXAM_SUPPORTER) && + exam.supporter.contains(user.uuid); + final BooleanSupplier isExamSupporter = () -> supporting || user.hasRole(UserRole.EXAM_ADMIN); + + final Composite content = this.pageService.getWidgetFactory().defaultPageLayout( + pageContext.getParent(), + new LocTextKey(TITLE_TEXT_KEY.name, exam.getName())); + + final TableBuilder tableBuilder = + this.pageService.entityTableBuilder(restService.getRestCall(GetFinishedExamClientConnectionPage.class)) + .withEmptyMessage(EMPTY_LIST_TEXT_KEY) + .withPaging(this.pageSize) + .withStaticFilter(ClientConnection.FILTER_ATTR_EXAM_ID, examKey.modelId) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID, + TABLE_COLUMN_NAME, + c -> c.clientConnection.getUserSessionId()) + .withFilter(this.nameFilter) + .sortable()) + + .withColumn(new ColumnDefinition( + ClientConnection.ATTR_INFO, + TABLE_COLUMN_INFO, + c -> c.clientConnection.getInfo()) + .withFilter(this.infoFilter) + .sortable()) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_CONNECTION.ATTR_STATUS, + TABLE_COLUMN_STATUS, + row -> this.pageService.getResourceService() + .localizedClientConnectionStatusName(row.clientConnection.getStatus())) + .withFilter(this.statusFilter) + .sortable()) + + .withDefaultAction(t -> actionBuilder + .newAction(ActionDefinition.VIEW_FINISHED_EXAM_CLIENT_CONNECTION) + .withParentEntityKey(examKey) + .create()) + .withSelectionListener(this.pageService.getSelectionPublisher( + pageContext, + ActionDefinition.VIEW_FINISHED_EXAM_CLIENT_CONNECTION)); + + indicators.stream().forEach(indicator -> { + if (indicator.type == IndicatorType.LAST_PING || indicator.type == IndicatorType.NONE) { + return; + } + tableBuilder.withColumn(new ColumnDefinition( + ClientConnectionData.ATTR_INDICATOR_VALUE + Constants.UNDERLINE + indicator.id, + new LocTextKey(indicator.name), + cc -> cc.getIndicatorDisplayValue(indicator)) + .sortable()); + }); + + final EntityTable table = tableBuilder.compose(pageContext.copyOf(content)); + + actionBuilder + + .newAction(ActionDefinition.VIEW_FINISHED_EXAM_CLIENT_CONNECTION) + .withParentEntityKey(examKey) + .withSelect( + table::getMultiSelection, + PageAction::applySingleSelectionAsEntityKey, + EMPTY_SELECTION_TEXT_KEY) + .publishIf(isExamSupporter, false); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExamClientConnection.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExamClientConnection.java new file mode 100644 index 00000000..03a042e4 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExamClientConnection.java @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2022 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.monitoring; + +import java.util.Collection; +import java.util.function.BooleanSupplier; + +import org.eclipse.swt.widgets.Composite; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; +import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; +import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; +import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; +import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; +import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; +import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent; +import ch.ethz.seb.sebserver.gbl.model.session.IndicatorValue; +import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamClientConnection; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute; +import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; + +@Lazy +@Component +@GuiProfile +public class FinishedExamClientConnection implements TemplateComposer { + + private static final LocTextKey PAGE_TITLE_KEY = + new LocTextKey("sebserver.finished.exam.connection.title"); + + private final static LocTextKey EXAM_NAME_TEXT_KEY = + new LocTextKey("sebserver.finished.connection.form.exam"); + private final static LocTextKey CONNECTION_ID_TEXT_KEY = + new LocTextKey("sebserver.finished.connection.form.id"); + private final static LocTextKey CONNECTION_INFO_TEXT_KEY = + new LocTextKey("sebserver.finished.connection.form.info"); + private final static LocTextKey CONNECTION_STATUS_TEXT_KEY = + new LocTextKey("sebserver.finished.connection.form.status"); + + private static final LocTextKey EVENT_LIST_TITLE_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.title"); + private static final LocTextKey EVENT_LIST_TITLE_TOOLTIP_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.title.tooltip"); + private static final LocTextKey EMPTY_LIST_TEXT_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.empty"); + private static final LocTextKey LIST_COLUMN_TYPE_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.type"); + + private static final LocTextKey LIST_COLUMN_CLIENT_TIME_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.clienttime"); + private static final LocTextKey LIST_COLUMN_SERVER_TIME_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.servertime"); + private static final LocTextKey LIST_COLUMN_VALUE_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.value"); + private static final LocTextKey LIST_COLUMN_TEXT_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.text"); + + private final PageService pageService; + private final ResourceService resourceService; + private final I18nSupport i18nSupport; + private final SEBClientEventDetailsPopup sebClientLogDetailsPopup; + private final int pageSize; + + private final TableFilterAttribute typeFilter; + private final TableFilterAttribute textFilter = + new TableFilterAttribute(CriteriaType.TEXT, ClientEvent.FILTER_ATTR_TEXT); + + protected FinishedExamClientConnection( + final PageService pageService, + final SEBClientEventDetailsPopup sebClientLogDetailsPopup, + @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { + + this.pageService = pageService; + this.resourceService = pageService.getResourceService(); + this.i18nSupport = this.resourceService.getI18nSupport(); + this.sebClientLogDetailsPopup = sebClientLogDetailsPopup; + this.pageSize = pageSize; + + this.typeFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Domain.CLIENT_EVENT.ATTR_TYPE, + this.resourceService::clientEventTypeResources); + } + + @Override + public void compose(final PageContext pageContext) { + final RestService restService = this.resourceService.getRestService(); + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + final CurrentUser currentUser = this.resourceService.getCurrentUser(); + final EntityKey parentEntityKey = pageContext.getParentEntityKey(); + final EntityKey entityKey = pageContext.getEntityKey(); + + // content page layout with title + final Composite content = widgetFactory.defaultPageLayout( + pageContext.getParent(), + PAGE_TITLE_KEY); + final Exam exam = restService + .getBuilder(GetExam.class) + .withURIVariable(API.PARAM_MODEL_ID, parentEntityKey.modelId) + .call() + .onError(error -> pageContext.notifyLoadError(EntityType.EXAM, error)) + .getOrThrow(); + final UserInfo user = currentUser.get(); + final boolean supporting = user.hasRole(UserRole.EXAM_SUPPORTER) && + exam.supporter.contains(user.uuid); + final BooleanSupplier isExamSupporter = () -> supporting || user.hasRole(UserRole.EXAM_ADMIN); + final Collection indicators = restService + .getBuilder(GetIndicators.class) + .withQueryParam(Indicator.FILTER_ATTR_EXAM_ID, parentEntityKey.modelId) + .call() + .getOrThrow(); + final ClientConnectionData connectionData = restService + .getBuilder(GetFinishedExamClientConnection.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .getOrThrow(); + + final FormBuilder formBuilder = this.pageService.formBuilder(pageContext.copyOf(content)) + .readonly(true) + .addField(FormBuilder.text( + QuizData.QUIZ_ATTR_NAME, + EXAM_NAME_TEXT_KEY, + exam.getName())) + .addField(FormBuilder.text( + Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID, + CONNECTION_ID_TEXT_KEY, + connectionData.clientConnection.userSessionId)) + .addField(FormBuilder.text( + ClientConnection.ATTR_INFO, + CONNECTION_INFO_TEXT_KEY, + connectionData.clientConnection.info)) + .withDefaultSpanInput(3) + .addField(FormBuilder.text( + Domain.CLIENT_CONNECTION.ATTR_STATUS, + CONNECTION_STATUS_TEXT_KEY, + this.resourceService.localizedClientConnectionStatusName( + connectionData.clientConnection.status)) + .asColorBox()) + .addEmptyCell(); + + indicators.forEach(indicator -> { + if (indicator.type == IndicatorType.LAST_PING || indicator.type == IndicatorType.NONE) { + return; + } + formBuilder.addField(FormBuilder.text( + indicator.name, + new LocTextKey(indicator.name), + connectionData.indicatorValues + .stream() + .filter(indicatorValue -> indicatorValue.getIndicatorId().equals(indicator.id)) + .findFirst() + .map(iv -> IndicatorValue.getDisplayValue(iv, indicator.type)) + .orElse(Constants.EMPTY_NOTE)) + .asColorBox() + .withDefaultLabel(indicator.name)) + .addEmptyCell(); + }); + + formBuilder.build(); + + // CLIENT EVENTS + final PageService.PageActionBuilder actionBuilder = this.pageService + .pageActionBuilder( + pageContext + .clearAttributes() + .clearEntityKeys()); + + widgetFactory.addFormSubContextHeader( + content, + EVENT_LIST_TITLE_KEY, + EVENT_LIST_TITLE_TOOLTIP_KEY); + + // client event table for this connection + this.pageService + .entityTableBuilder( + "seb-client-" + connectionData.getModelId(), + restService.getRestCall(GetExtendedClientEventPage.class)) + .withEmptyMessage(EMPTY_LIST_TEXT_KEY) + .withPaging(this.pageSize) + .withRestCallAdapter(restCallBuilder -> restCallBuilder.withQueryParam( + ClientEvent.FILTER_ATTR_CONNECTION_ID, + entityKey.modelId)) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_EVENT.ATTR_TYPE, + LIST_COLUMN_TYPE_KEY, + this.resourceService::getEventTypeName) + .withFilter(this.typeFilter) + .sortable() + .widthProportion(2)) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_EVENT.ATTR_TEXT, + LIST_COLUMN_TEXT_KEY, + ClientEvent::getText) + .withFilter(this.textFilter) + .sortable() + .withCellTooltip() + .widthProportion(4)) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_EVENT.ATTR_NUMERIC_VALUE, + LIST_COLUMN_VALUE_KEY, + ClientEvent::getValue) + .widthProportion(1)) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_EVENT.ATTR_CLIENT_TIME, + new LocTextKey(LIST_COLUMN_CLIENT_TIME_KEY.name, + this.i18nSupport.getUsersTimeZoneTitleSuffix()), + this::getClientTime) + .sortable() + .widthProportion(1)) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_EVENT.ATTR_SERVER_TIME, + new LocTextKey(LIST_COLUMN_SERVER_TIME_KEY.name, + this.i18nSupport.getUsersTimeZoneTitleSuffix()), + this::getServerTime) + .sortable() + .widthProportion(1)) + + .withDefaultAction(t -> actionBuilder + .newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS) + .withExec(action -> this.sebClientLogDetailsPopup.showDetails(action, + t.getSingleSelectedROWData())) + .noEventPropagation() + .create()) + + .compose(pageContext.copyOf(content)); + + actionBuilder + .newAction(ActionDefinition.FINISHED_EXAM_BACK_TO_OVERVIEW) + .withEntityKey(parentEntityKey) + .publishIf(isExamSupporter); + } + + private String getClientTime(final ClientEvent event) { + if (event == null || event.getClientTime() == null) { + return Constants.EMPTY_NOTE; + } + + return this.i18nSupport + .formatDisplayTime(Utils.toDateTimeUTC(event.getClientTime())); + } + + private String getServerTime(final ClientEvent event) { + if (event == null || event.getServerTime() == null) { + return Constants.EMPTY_NOTE; + } + + return this.i18nSupport + .formatDisplayTime(Utils.toDateTimeUTC(event.getServerTime())); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExamList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExamList.java new file mode 100644 index 00000000..976ecf3d --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExamList.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2022 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.monitoring; + +import org.eclipse.swt.widgets.Composite; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.content.exam.ExamList; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute; +import ch.ethz.seb.sebserver.gui.table.EntityTable; +import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; + +@Lazy +@Component +@GuiProfile +public class FinishedExamList implements TemplateComposer { + + private static final LocTextKey PAGE_TITLE_KEY = + new LocTextKey("sebserver.finished.exam.list.title"); + private final static LocTextKey EMPTY_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.finished.exam.info.pleaseSelect"); + private final static LocTextKey COLUMN_TITLE_NAME_KEY = + new LocTextKey("sebserver.finished.exam.list.column.name"); + private final static LocTextKey COLUMN_TITLE_STATE_KEY = + new LocTextKey("sebserver.finished.exam.list.column.state"); + private final static LocTextKey COLUMN_TITLE_TYPE_KEY = + new LocTextKey("sebserver.finished.exam.list.column.type"); + private final static LocTextKey EMPTY_LIST_TEXT_KEY = + new LocTextKey("sebserver.finished.exam.list.empty"); + + private final TableFilterAttribute nameFilter = + new TableFilterAttribute(CriteriaType.TEXT, QuizData.FILTER_ATTR_NAME); + private final TableFilterAttribute typeFilter; + private final TableFilterAttribute stateFilter; + + private final PageService pageService; + private final ResourceService resourceService; + private final int pageSize; + + protected FinishedExamList( + final PageService pageService, + @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { + + this.pageService = pageService; + this.resourceService = pageService.getResourceService(); + this.pageSize = pageSize; + + this.typeFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Exam.FILTER_ATTR_TYPE, + this.resourceService::examTypeResources); + + this.stateFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Exam.FILTER_ATTR_STATUS, + this.resourceService::localizedFinishedExamStatusSelection); + } + + @Override + public void compose(final PageContext pageContext) { + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + final CurrentUser currentUser = this.resourceService.getCurrentUser(); + final RestService restService = this.resourceService.getRestService(); + final I18nSupport i18nSupport = this.resourceService.getI18nSupport(); + + // content page layout with title + final Composite content = widgetFactory.defaultPageLayout( + pageContext.getParent(), + PAGE_TITLE_KEY); + + final PageActionBuilder actionBuilder = this.pageService + .pageActionBuilder(pageContext.clearEntityKeys()); + + // table + final EntityTable table = + this.pageService.entityTableBuilder(restService.getRestCall(GetFinishedExamPage.class)) + .withEmptyMessage(EMPTY_LIST_TEXT_KEY) + .withPaging(this.pageSize) + .withRowDecorator(ExamList.decorateOnExamConsistency(this.pageService)) + .withDefaultSort(QuizData.QUIZ_ATTR_NAME) + + .withColumn(new ColumnDefinition<>( + QuizData.QUIZ_ATTR_NAME, + COLUMN_TITLE_NAME_KEY, + Exam::getName) + .withFilter(this.nameFilter) + .sortable()) + + .withColumn(new ColumnDefinition<>( + Domain.EXAM.ATTR_STATUS, + COLUMN_TITLE_STATE_KEY, + this.resourceService::localizedExamStatusName) + .withFilter(this.stateFilter) + .sortable()) + + .withColumn(new ColumnDefinition( + Domain.EXAM.ATTR_TYPE, + COLUMN_TITLE_TYPE_KEY, + this.resourceService::localizedExamTypeName) + .withFilter(this.typeFilter) + .sortable()) + + .withColumn(new ColumnDefinition<>( + QuizData.QUIZ_ATTR_START_TIME, + new LocTextKey( + "sebserver.finished.exam.list.column.startTime", + i18nSupport.getUsersTimeZoneTitleSuffix()), + Exam::getStartTime) + .sortable()) + + .withColumn(new ColumnDefinition<>( + QuizData.QUIZ_ATTR_END_TIME, + new LocTextKey( + "sebserver.finished.exam.list.column.endTime", + i18nSupport.getUsersTimeZoneTitleSuffix()), + Exam::getEndTime) + .sortable()) + + .withDefaultAction(actionBuilder + .newAction(ActionDefinition.VIEW_FINISHED_EXAM_FROM_LIST) + .create()) + + .withSelectionListener(this.pageService.getSelectionPublisher( + pageContext, + ActionDefinition.VIEW_FINISHED_EXAM_FROM_LIST)) + + .compose(pageContext.copyOf(content)); + + actionBuilder + + .newAction(ActionDefinition.VIEW_FINISHED_EXAM_FROM_LIST) + .withSelect( + table::getMultiSelection, + PageAction::applySingleSelectionAsEntityKey, + EMPTY_SELECTION_TEXT_KEY) + .publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER), false); + + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java index 5a18104a..b0ab27d3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java @@ -8,7 +8,9 @@ package ch.ethz.seb.sebserver.gui.content.monitoring; +import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; import java.util.function.BooleanSupplier; import java.util.function.Supplier; @@ -52,8 +54,8 @@ import ch.ethz.seb.sebserver.gui.service.push.UpdateErrorHandler; 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.exam.GetExam; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.ConfirmPendingClientNotification; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionData; @@ -90,6 +92,11 @@ public class MonitoringClientConnection implements TemplateComposer { private static final LocTextKey NOTIFICATION_LIST_COLUMN_TYPE_KEY = new LocTextKey("sebserver.monitoring.exam.connection.notificationlist.type"); + private static final LocTextKey CONFIRM_QUIT = + new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit.confirm"); + private static final LocTextKey CONFIRM_OPEN_SINGLE_ROOM = + new LocTextKey("sebserver.monitoring.exam.connection.action.singleroom.confirm"); + private static final LocTextKey EVENT_LIST_TITLE_KEY = new LocTextKey("sebserver.monitoring.exam.connection.eventlist.title"); private static final LocTextKey EVENT_LIST_TITLE_TOOLTIP_KEY = @@ -107,10 +114,6 @@ public class MonitoringClientConnection implements TemplateComposer { new LocTextKey("sebserver.monitoring.exam.connection.eventlist.value"); private static final LocTextKey LIST_COLUMN_TEXT_KEY = new LocTextKey("sebserver.monitoring.exam.connection.eventlist.text"); - private static final LocTextKey CONFIRM_QUIT = - new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit.confirm"); - private static final LocTextKey CONFIRM_OPEN_SINGLE_ROOM = - new LocTextKey("sebserver.monitoring.exam.connection.action.singleroom.confirm"); private final ServerPushService serverPushService; private final PageService pageService; @@ -118,6 +121,7 @@ public class MonitoringClientConnection implements TemplateComposer { private final I18nSupport i18nSupport; private final InstructionProcessor instructionProcessor; private final SEBClientEventDetailsPopup sebClientLogDetailsPopup; + private final SEBSendLockPopup sebSendLockPopup; private final MonitoringProctoringService monitoringProctoringService; private final long pollInterval; private final int pageSize; @@ -132,6 +136,7 @@ public class MonitoringClientConnection implements TemplateComposer { final InstructionProcessor instructionProcessor, final SEBClientEventDetailsPopup sebClientLogDetailsPopup, final MonitoringProctoringService monitoringProctoringService, + final SEBSendLockPopup sebSendLockPopup, @Value("${sebserver.gui.webservice.poll-interval:500}") final long pollInterval, @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { @@ -143,6 +148,7 @@ public class MonitoringClientConnection implements TemplateComposer { this.monitoringProctoringService = monitoringProctoringService; this.pollInterval = pollInterval; this.sebClientLogDetailsPopup = sebClientLogDetailsPopup; + this.sebSendLockPopup = sebSendLockPopup; this.pageSize = pageSize; this.typeFilter = new TableFilterAttribute( @@ -263,7 +269,7 @@ public class MonitoringClientConnection implements TemplateComposer { .withParentEntityKey(parentEntityKey) .withConfirm(() -> NOTIFICATION_LIST_CONFIRM_TEXT_KEY) .withSelect( - () -> notificationTable.getSelection(), + () -> notificationTable.getMultiSelection(), action -> this.confirmNotification(action, connectionData, notificationTable), NOTIFICATION_LIST_NO_SELECTION_KEY) @@ -275,14 +281,11 @@ public class MonitoringClientConnection implements TemplateComposer { final Supplier> notificationTableSupplier = _notificationTableSupplier; // server push update - final UpdateErrorHandler updateErrorHandler = - new UpdateErrorHandler(this.pageService, pageContext); - this.serverPushService.runServerPush( new ServerPushContext( content, Utils.truePredicate(), - updateErrorHandler), + new UpdateErrorHandler(this.pageService, pageContext)), this.pollInterval, context -> clientConnectionDetails.updateData(), context -> clientConnectionDetails.updateGUI(notificationTableSupplier, pageContext)); @@ -367,18 +370,26 @@ public class MonitoringClientConnection implements TemplateComposer { .withConfirm(() -> CONFIRM_QUIT) .withExec(action -> { this.instructionProcessor.propagateSEBQuitInstruction( - exam.id, + exam.getModelId(), connectionToken, pageContext); return action; }) .noEventPropagation() + .publishIf(() -> isExamSupporter.getAsBoolean() && + connectionData.clientConnection.status.clientActiveStatus) + + .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_LOCK) + .withEntityKey(parentEntityKey) + .withExec(action -> this.sebSendLockPopup.show(action, + some -> new HashSet<>(Arrays.asList(connectionToken)))) + .noEventPropagation() .publishIf(() -> isExamSupporter.getAsBoolean() && connectionData.clientConnection.status.clientActiveStatus); if (connectionData.clientConnection.status == ConnectionStatus.ACTIVE) { final ProctoringServiceSettings proctoringSettings = restService - .getBuilder(GetProctoringSettings.class) + .getBuilder(GetExamProctoringSettings.class) .withURIVariable(API.PARAM_MODEL_ID, parentEntityKey.modelId) .call() .onError(error -> log.error("Failed to get ProctoringServiceSettings", error)) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringExamSearchPopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringExamSearchPopup.java index 6046c9d3..30098e10 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringExamSearchPopup.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringExamSearchPopup.java @@ -69,7 +69,7 @@ public class MonitoringExamSearchPopup { final ModalInputDialog dialog = new ModalInputDialog<>( pageContext.getParent().getShell(), this.pageService.getWidgetFactory()); - dialog.setLargeDialogWidth(); + dialog.setVeryLargeDialogWidth(); dialog.setDialogHeight(380); dialog.open( TITLE_TEXT_KEY, @@ -93,20 +93,23 @@ public class MonitoringExamSearchPopup { Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID, TABLE_COLUMN_NAME, ClientConnection::getUserSessionId) - .withFilter(this.nameFilter)) + .withFilter(this.nameFilter) + .widthProportion(2)) .withColumn(new ColumnDefinition<>( ClientConnection.ATTR_INFO, TABLE_COLUMN_INFO, ClientConnection::getInfo) - .withFilter(this.infoFilter)) + .withFilter(this.infoFilter) + .widthProportion(3)) .withColumn(new ColumnDefinition( Domain.CLIENT_CONNECTION.ATTR_STATUS, TABLE_COLUMN_STATUS, row -> this.pageService.getResourceService() .localizedClientConnectionStatusName(row.getStatus())) - .withFilter(this.statusFilter)) + .withFilter(this.statusFilter) + .widthProportion(1)) .withDefaultAction(t -> actionBuilder .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java index aa61abd4..01d02f2d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java @@ -13,6 +13,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Set; import java.util.function.BooleanSupplier; +import java.util.function.Consumer; import java.util.function.Function; import org.eclipse.swt.SWT; @@ -54,8 +55,8 @@ import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; import ch.ethz.seb.sebserver.gui.service.push.ServerPushService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionTable; import ch.ethz.seb.sebserver.gui.service.session.FullPageMonitoringGUIUpdate; @@ -70,8 +71,6 @@ import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService @GuiProfile public class MonitoringRunningExam implements TemplateComposer { - //private static final Logger log = LoggerFactory.getLogger(MonitoringRunningExam.class); - private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = new LocTextKey("sebserver.monitoring.exam.connection.emptySelection"); private static final LocTextKey EMPTY_ACTIVE_SELECTION_TEXT_KEY = @@ -94,16 +93,18 @@ public class MonitoringRunningExam implements TemplateComposer { private final AsyncRunner asyncRunner; private final InstructionProcessor instructionProcessor; private final MonitoringExamSearchPopup monitoringExamSearchPopup; + private final SEBSendLockPopup sebSendLockPopup; private final MonitoringProctoringService monitoringProctoringService; private final boolean distributedSetup; private final long pollInterval; - protected MonitoringRunningExam( + public MonitoringRunningExam( final ServerPushService serverPushService, final PageService pageService, final AsyncRunner asyncRunner, final InstructionProcessor instructionProcessor, final MonitoringExamSearchPopup monitoringExamSearchPopup, + final SEBSendLockPopup sebSendLockPopup, final MonitoringProctoringService monitoringProctoringService, final GuiServiceInfo guiServiceInfo, @Value("${sebserver.gui.webservice.poll-interval:2000}") final long pollInterval) { @@ -118,14 +119,14 @@ public class MonitoringRunningExam implements TemplateComposer { this.pollInterval = pollInterval; this.distributedSetup = guiServiceInfo.isDistributedSetup(); this.monitoringExamSearchPopup = monitoringExamSearchPopup; + this.sebSendLockPopup = sebSendLockPopup; } @Override public void compose(final PageContext pageContext) { - final RestService restService = this.resourceService.getRestService(); final EntityKey entityKey = pageContext.getEntityKey(); final CurrentUser currentUser = this.resourceService.getCurrentUser(); - final Exam exam = restService.getBuilder(GetExam.class) + final Exam exam = this.restService.getBuilder(GetExam.class) .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) .call() .getOrThrow(); @@ -134,7 +135,7 @@ public class MonitoringRunningExam implements TemplateComposer { exam.supporter.contains(user.uuid); final BooleanSupplier isExamSupporter = () -> supporting || user.hasRole(UserRole.EXAM_ADMIN); - final Collection indicators = restService.getBuilder(GetIndicators.class) + final Collection indicators = this.restService.getBuilder(GetIndicators.class) .withQueryParam(Indicator.FILTER_ATTR_EXAM_ID, entityKey.modelId) .call() .getOrThrow(); @@ -175,15 +176,48 @@ public class MonitoringRunningExam implements TemplateComposer { .withParentEntityKey(entityKey) .create(), this.pageService) - .withSelectionListener(this.pageService.getSelectionPublisher( + .withSelectionListener(this.getSelectionPublisherClientConnectionTable( pageContext, ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION, ActionDefinition.MONITOR_EXAM_QUIT_SELECTED, + ActionDefinition.MONITOR_EXAM_LOCK_SELECTED, ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION, ActionDefinition.MONITOR_EXAM_NEW_PROCTOR_ROOM)); actionBuilder + .newAction(ActionDefinition.MONITORING_EXAM_SEARCH_CONNECTIONS) + .withEntityKey(entityKey) + .withExec(this::openSearchPopup) + .noEventPropagation() + .publishIf(isExamSupporter) + + .newAction(ActionDefinition.MONITOR_EXAM_QUIT_ALL) + .withEntityKey(entityKey) + .withConfirm(() -> CONFIRM_QUIT_ALL) + .withExec(action -> this.quitSEBClients(action, clientTable, true)) + .noEventPropagation() + .publishIf(isExamSupporter) + + .newAction(ActionDefinition.MONITOR_EXAM_QUIT_SELECTED) + .withEntityKey(entityKey) + .withConfirm(() -> CONFIRM_QUIT_SELECTED) + .withSelect( + () -> this.selectionForInstruction(clientTable), + action -> this.quitSEBClients(action, clientTable, false), + EMPTY_ACTIVE_SELECTION_TEXT_KEY) + .noEventPropagation() + .publishIf(isExamSupporter, false) + + .newAction(ActionDefinition.MONITOR_EXAM_LOCK_SELECTED) + .withEntityKey(entityKey) + .withSelect( + () -> this.selectionForInstruction(clientTable), + action -> this.showSEBLockActionPopup(action, clientTable), + EMPTY_ACTIVE_SELECTION_TEXT_KEY) + .noEventPropagation() + .publishIf(isExamSupporter, false) + .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION) .withParentEntityKey(entityKey) .withExec(pageAction -> { @@ -204,29 +238,6 @@ public class MonitoringRunningExam implements TemplateComposer { }) .publishIf(isExamSupporter, false) - .newAction(ActionDefinition.MONITOR_EXAM_QUIT_ALL) - .withEntityKey(entityKey) - .withConfirm(() -> CONFIRM_QUIT_ALL) - .withExec(action -> this.quitSEBClients(action, clientTable, true)) - .noEventPropagation() - .publishIf(isExamSupporter) - - .newAction(ActionDefinition.MONITORING_EXAM_SEARCH_CONNECTIONS) - .withEntityKey(entityKey) - .withExec(this::openSearchPopup) - .noEventPropagation() - .publishIf(isExamSupporter) - - .newAction(ActionDefinition.MONITOR_EXAM_QUIT_SELECTED) - .withEntityKey(entityKey) - .withConfirm(() -> CONFIRM_QUIT_SELECTED) - .withSelect( - () -> this.selectionForQuitInstruction(clientTable), - action -> this.quitSEBClients(action, clientTable, false), - EMPTY_ACTIVE_SELECTION_TEXT_KEY) - .noEventPropagation() - .publishIf(isExamSupporter, false) - .newAction(ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION) .withEntityKey(entityKey) .withConfirm(() -> CONFIRM_DISABLE_SELECTED) @@ -245,7 +256,7 @@ public class MonitoringRunningExam implements TemplateComposer { isExamSupporter)); final ProctoringServiceSettings proctoringSettings = this.restService - .getBuilder(GetProctoringSettings.class) + .getBuilder(GetExamProctoringSettings.class) .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) .call() .getOr(null); @@ -264,6 +275,19 @@ public class MonitoringRunningExam implements TemplateComposer { fullPageMonitoringUpdate.start(pageContext, content, this.pollInterval); } + private PageAction showSEBLockActionPopup( + final PageAction action, + final ClientConnectionTable clientTable) { + + this.sebSendLockPopup.show( + action, + statesPredicate -> clientTable.getConnectionTokens( + statesPredicate, + true)); + clientTable.removeSelection(); + return action; + } + private FullPageMonitoringGUIUpdate createProctoringActions( final ProctoringServiceSettings proctoringSettings, final ProctoringGUIService proctoringGUIService, @@ -463,7 +487,7 @@ public class MonitoringRunningExam implements TemplateComposer { }; } - private Set selectionForQuitInstruction(final ClientConnectionTable clientTable) { + private Set selectionForInstruction(final ClientConnectionTable clientTable) { final Set connectionTokens = clientTable.getConnectionTokens( cc -> cc.status.clientActiveStatus, true); @@ -480,7 +504,7 @@ public class MonitoringRunningExam implements TemplateComposer { final boolean all) { this.instructionProcessor.propagateSEBQuitInstruction( - clientTable.getExam().id, + clientTable.getExam().getModelId(), statesPredicate -> clientTable.getConnectionTokens( statesPredicate, !all), @@ -508,4 +532,13 @@ public class MonitoringRunningExam implements TemplateComposer { return action; } + private Consumer getSelectionPublisherClientConnectionTable( + final PageContext pageContext, + final ActionDefinition... actionDefinitions) { + + return table -> this.pageService.firePageEvent( + new ActionActivationEvent(table.getSingleSelection() != null, actionDefinitions), + pageContext); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExamList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExamList.java index be50dd72..5ebdcef8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExamList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExamList.java @@ -141,7 +141,10 @@ public class MonitoringRunningExamList implements TemplateComposer { actionBuilder .newAction(ActionDefinition.MONITOR_EXAM_FROM_LIST) - .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) + .withSelect( + table::getMultiSelection, + PageAction::applySingleSelectionAsEntityKey, + EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER), false); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/SEBClientEventDetailsPopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/SEBClientEventDetailsPopup.java index 3659b9de..32b306a1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/SEBClientEventDetailsPopup.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/SEBClientEventDetailsPopup.java @@ -215,7 +215,7 @@ public class SEBClientEventDetailsPopup { .addField(FormBuilder.text( QuizData.QUIZ_ATTR_DESCRIPTION, FORM_DESC_TEXT_KEY, - exam.description) + exam.getDescription()) .asArea() .asHTML(true)) .addField(FormBuilder.text( diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/SEBClientEvents.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/SEBClientEvents.java index b522ac71..083dec9d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/SEBClientEvents.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/SEBClientEvents.java @@ -227,7 +227,7 @@ public class SEBClientEvents implements TemplateComposer { actionBuilder .newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS) .withSelect( - table::getSelection, + table::getMultiSelection, action -> this.sebClientEventDetailsPopup.showDetails(action, table.getSingleSelectedROWData()), EMPTY_SELECTION_TEXT) .noEventPropagation() diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/SEBSendLockPopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/SEBSendLockPopup.java new file mode 100644 index 00000000..ae3437d4 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/SEBSendLockPopup.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2022 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.monitoring; + +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import org.apache.tomcat.util.buf.StringUtils; +import org.eclipse.swt.widgets.Composite; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; +import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; +import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.form.FormHandle; +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.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionPage; +import ch.ethz.seb.sebserver.gui.service.session.InstructionProcessor; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; + +@Lazy +@Component +@GuiProfile +public class SEBSendLockPopup { + + private static final LocTextKey TITLE_TEXT_KEY = + new LocTextKey("sebserver.monitoring.lock.title"); + private static final LocTextKey FORM_INFO_TITLE = + new LocTextKey("sebserver.monitoring.lock.form.info.title"); + private static final LocTextKey FORM_INFO = + new LocTextKey("sebserver.monitoring.lock.form.info"); + private static final LocTextKey FORM_MESSAGE = + new LocTextKey("sebserver.monitoring.lock.form.message"); + + private static final LocTextKey TABLE_TITLE = + new LocTextKey("sebserver.monitoring.lock.list.title"); + private static final LocTextKey TABLE_COLUMN_NAME = + new LocTextKey("sebserver.monitoring.lock.list.name"); + private static final LocTextKey TABLE_COLUMN_INFO = + new LocTextKey("sebserver.monitoring.lock.list.info"); + + private final PageService pageService; + private final InstructionProcessor instructionProcessor; + + protected SEBSendLockPopup( + final PageService pageService, + final InstructionProcessor instructionProcessor) { + + this.pageService = pageService; + this.instructionProcessor = instructionProcessor; + } + + public PageAction show( + final PageAction action, + final Function, Set> selectionFunction) { + + try { + + final PageContext pageContext = action.pageContext(); + final Set selection = selectionFunction.apply(ClientConnection.getStatusPredicate( + ConnectionStatus.CONNECTION_REQUESTED, + ConnectionStatus.ACTIVE)); + + if (selection == null || selection.isEmpty()) { + action + .pageContext() + .publishInfo(new LocTextKey("sebserver.monitoring.lock.noselection")); + return action; + } + + final String connectionTokens = StringUtils.join(selection, Constants.LIST_SEPARATOR_CHAR); + final boolean showList = selection.size() > 1; + final PopupComposer popupComposer = new PopupComposer( + this.pageService, + pageContext, + connectionTokens, + showList); + + final ModalInputDialog> dialog = + new ModalInputDialog>( + action.pageContext().getParent().getShell(), + this.pageService.getWidgetFactory()) + .setDialogWidth(800) + .setDialogHeight(showList ? 500 : 200); + + final Predicate> doLock = formHandle -> propagateLockInstruction( + connectionTokens, + pageContext, + formHandle); + + dialog.open( + TITLE_TEXT_KEY, + doLock, + Utils.EMPTY_EXECUTION, + popupComposer); + } catch (final Exception e) { + action.pageContext().notifyUnexpectedError(e); + } + return action; + } + + private final class PopupComposer implements ModalInputDialogComposer> { + + private final PageService pageService; + private final PageContext pageContext; + private final String connectionTokens; + private final boolean showList; + + protected PopupComposer( + final PageService pageService, + final PageContext pageContext, + final String connectionTokens, + final boolean showList) { + + this.pageService = pageService; + this.pageContext = pageContext; + this.connectionTokens = connectionTokens; + this.showList = showList; + } + + @Override + public Supplier> compose(final Composite parent) { + final EntityKey examKey = this.pageContext.getEntityKey(); + final RestService restService = this.pageService.getRestService(); + + final PageContext formContext = this.pageContext.copyOf(parent); + final FormHandle form = this.pageService.formBuilder(formContext) + .addField(FormBuilder.text( + "Info", + FORM_INFO_TITLE, + this.pageService.getI18nSupport().getText(FORM_INFO)) + .asArea(50) + .asHTML() + .readonly(true)) + .addField(FormBuilder.text( + ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_FORCE_LOCK_SCREEN.MESSAGE, + FORM_MESSAGE) + .asArea(50) + .asHTML()) + .build(); + + if (this.showList) { + this.pageService + .getWidgetFactory() + .labelLocalized(parent, CustomVariant.TEXT_H3, TABLE_TITLE); + + // table of selected SEB connections + this.pageService + .entityTableBuilder(restService.getRestCall(GetClientConnectionPage.class)) + .withStaticFilter( + ClientConnection.FILTER_ATTR_TOKEN_LIST, + this.connectionTokens) + .withStaticFilter( + ClientConnection.FILTER_ATTR_EXAM_ID, + examKey.modelId) + .withPaging(10) + + .withColumn(new ColumnDefinition<>( + Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID, + TABLE_COLUMN_NAME, + ClientConnection::getUserSessionId)) + + .withColumn(new ColumnDefinition<>( + ClientConnection.ATTR_INFO, + TABLE_COLUMN_INFO, + ClientConnection::getInfo)) + + .compose(formContext); + } + + return () -> form; + } + } + + private boolean propagateLockInstruction( + final String connectionTokens, + final PageContext pageContext, + final FormHandle formHandle) { + + try { + + final EntityKey examKey = pageContext.getEntityKey(); + final String lockMessage = formHandle + .getForm() + .getFieldValue(ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_FORCE_LOCK_SCREEN.MESSAGE); + + this.instructionProcessor.propagateSEBLockInstruction( + examKey.modelId, + lockMessage, + null, + connectionTokens, + pageContext); + + } catch (final Exception e) { + pageContext.notifyUnexpectedError(e); + } + + return true; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java index cd44d9ab..02a90892 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java @@ -70,7 +70,7 @@ public final class Form implements FormBinding { flush(); return this.jsonMapper.writeValueAsString(this.objectRoot); } catch (final Exception e) { - throw new RuntimeException("Unexpected error while trying to create json form Form post: ", e); + throw new RuntimeException("Unexpected error while trying to create json from form post: ", e); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/PasswordFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/PasswordFieldBuilder.java index b2210d6c..516e1994 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/PasswordFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/PasswordFieldBuilder.java @@ -36,7 +36,8 @@ public class PasswordFieldBuilder extends FieldBuilder { final PasswordInput input = new PasswordInput( fieldGrid, builder.widgetFactory, - getARIALabel(builder)); + getARIALabel(builder), + this.label); input.setEditable(!readonly); input.setValue((StringUtils.isNotBlank(this.value)) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java index 5a417d9e..f2120b0c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java @@ -34,6 +34,8 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; public final class SelectionFieldBuilder extends FieldBuilder { + private static final String TEST_KEY_SUFFIX = ".action"; + final Supplier>> itemsSupplier; Consumer
selectionListener = null; final Selection.Type type; @@ -69,14 +71,14 @@ public final class SelectionFieldBuilder extends FieldBuilder { private void buildInput(final FormBuilder builder, final Control titleLabel) { final Composite fieldGrid = createFieldGrid(builder.formParent, this.spanInput); - final String actionKey = (this.label != null) ? this.label.name + ".action" : null; + final String testKey = (this.label != null) ? this.label.name + TEST_KEY_SUFFIX : null; final Selection selection = builder.widgetFactory.selectionLocalized( this.type, fieldGrid, this.itemsSupplier, (builder.pageService.getFormTooltipMode() == PageService.FormTooltipMode.INPUT) ? this.tooltip : null, null, - actionKey, + testKey, builder.i18nSupport.getText(getARIALabel(builder))); final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); @@ -161,7 +163,12 @@ public final class SelectionFieldBuilder extends FieldBuilder { } if (this.label != null) { - WidgetFactory.setTestId(label, this.label.name + "_" + valueKey); + final String testKey = this.label.name + TEST_KEY_SUFFIX; + if (this.type == Type.MULTI || this.type == Type.MULTI_COMBO || this.type == Type.MULTI_CHECKBOX) { + WidgetFactory.setTestId(label, testKey + "_" + valueKey); + } else { + WidgetFactory.setTestId(label, testKey); + } } return label; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java index a085c914..c12bfc32 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java @@ -81,7 +81,6 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetIn import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetupNames; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.cert.GetCertificateNames; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNodeNames; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNodes; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetViews; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccountNames; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; @@ -119,6 +118,7 @@ public class ResourceService { public static final EnumSet CLIENT_EVENT_TYPE_EXCLUDE_MAP = EnumSet.of( EventType.UNKNOWN); + public static final String EXAM_STATUS_PREFIX = "sebserver.exam.status."; public static final String EXAMCONFIG_STATUS_PREFIX = "sebserver.examconfig.status."; public static final String EXAM_TYPE_PREFIX = "sebserver.exam.type."; public static final String USERACCOUNT_ROLE_PREFIX = "sebserver.useraccount.role."; @@ -471,7 +471,20 @@ public class ResourceService { .collect(Collectors.toList()); } - public List> examConfigStatusResources(final boolean isAttachedToExam) { + public List> examConfigStatusResourcesAll() { + return Arrays.stream(ConfigurationStatus.values()) + .map(type -> new Tuple3<>( + type.name(), + this.i18nSupport.getText(EXAMCONFIG_STATUS_PREFIX + type.name()), + Utils.formatLineBreaks(this.i18nSupport.getText( + this.i18nSupport.getText(EXAMCONFIG_STATUS_PREFIX + type.name()) + + Constants.TOOLTIP_TEXT_KEY_SUFFIX, + StringUtils.EMPTY)))) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public List> examConfigStatusResources(final boolean isAttachedToExam, final boolean hasRunningExam) { return Arrays.stream(ConfigurationStatus.values()) .filter(status -> { if (isAttachedToExam) { @@ -480,6 +493,7 @@ public class ResourceService { return status != ConfigurationStatus.IN_USE; } }) + .filter(status -> !hasRunningExam || status != ConfigurationStatus.ARCHIVED) .map(type -> new Tuple3<>( type.name(), this.i18nSupport.getText(EXAMCONFIG_STATUS_PREFIX + type.name()), @@ -535,6 +549,32 @@ public class ResourceService { .apply(String.valueOf(config.institutionId)); } + public List> localizedExamStatusSelection() { + return Arrays.stream(ExamStatus.values()) + .map(type -> new Tuple<>(type.name(), + this.i18nSupport.getText(EXAM_STATUS_PREFIX + type.name()))) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public List> localizedFinishedExamStatusSelection() { + return Arrays.stream(ExamStatus.values()) + .filter(st -> st == ExamStatus.ARCHIVED || st == ExamStatus.FINISHED) + .map(type -> new Tuple<>(type.name(), + this.i18nSupport.getText(EXAM_STATUS_PREFIX + type.name()))) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + + public String localizedExamStatusName(final Exam exam) { + if (exam.status == null) { + return Constants.EMPTY_NOTE; + } + + return this.i18nSupport + .getText(ResourceService.EXAM_STATUS_PREFIX + exam.status.name()); + } + public String localizedExamConfigStatusName(final ConfigurationNode config) { if (config.status == null) { return Constants.EMPTY_NOTE; @@ -629,9 +669,13 @@ public class ResourceService { .call() .getOr(Collections.emptyList()) .stream() - .filter(exam -> exam != null - && (exam.getStatus() == ExamStatus.RUNNING || exam.getStatus() == ExamStatus.FINISHED)) - .map(exam -> new Tuple<>(exam.getModelId(), exam.name)) + .filter(exam -> exam != null && + (exam.getStatus() == ExamStatus.RUNNING || + exam.getStatus() == ExamStatus.FINISHED || + exam.getStatus() == ExamStatus.ARCHIVED)) + .map(exam -> new Tuple<>( + exam.getModelId(), + StringUtils.isBlank(exam.name) ? exam.externalId : exam.name)) .sorted(RESOURCE_COMPARATOR) .collect(Collectors.toList()); } @@ -644,7 +688,10 @@ public class ResourceService { .call() .getOr(Collections.emptyList()) .stream() - .map(entityName -> new Tuple<>(entityName.modelId, entityName.name)) + .filter(entityName -> StringUtils.isNotBlank(entityName.modelId)) + .map(entityName -> new Tuple<>( + entityName.modelId, + StringUtils.isBlank(entityName.name) ? entityName.modelId : entityName.name)) .sorted(RESOURCE_COMPARATOR) .collect(Collectors.toList()); } @@ -660,7 +707,7 @@ public class ResourceService { .filter(k -> StringUtils.isNotBlank(k.modelId)) .collect(Collectors.toMap( k -> Long.valueOf(k.modelId), - k -> k.name)); + k -> StringUtils.isBlank(k.name) ? k.modelId : k.name)); } public List> getViewResources() { @@ -725,8 +772,16 @@ public class ResourceService { } public List> getExamConfigTemplateResources() { + return getExamConfigTemplateResourcesSelection(true); + } + + public List> getExamConfigTemplateResourcesSelection() { + return getExamConfigTemplateResourcesSelection(false); + } + + public List> getExamConfigTemplateResourcesSelection(final boolean withEmpty) { final UserInfo userInfo = this.currentUser.get(); - final List> collect = this.restService.getBuilder(GetExamConfigNodes.class) + final List> collect = this.restService.getBuilder(GetExamConfigNodeNames.class) .withQueryParam(Entity.FILTER_ATTR_INSTITUTION, String.valueOf(userInfo.getInstitutionId())) .withQueryParam(ConfigurationNode.FILTER_ATTR_TYPE, ConfigurationType.TEMPLATE.name()) .call() @@ -735,10 +790,27 @@ public class ResourceService { .map(node -> new Tuple<>(node.getModelId(), node.name)) .sorted(RESOURCE_COMPARATOR) .collect(Collectors.toList()); - collect.add(0, new Tuple<>(null, StringUtils.EMPTY)); + if (withEmpty) { + collect.add(0, new Tuple<>(null, StringUtils.EMPTY)); + } return collect; } + public final Function examConfigTemplateNameFunction() { + final List> examTemplateResources = getExamConfigTemplateResourcesSelection(); + return node -> { + if (node.templateId == null) { + return Constants.EMPTY_NOTE; + } + return examTemplateResources + .stream() + .filter(tuple -> node.templateId.toString().equals(tuple._1)) + .map(tuple -> tuple._2) + .findAny() + .orElse(Constants.EMPTY_NOTE); + }; + } + public List> sebRestrictionWhiteListResources() { return Arrays.stream(WhiteListPath.values()) .map(type -> new Tuple3<>( diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/CheckBoxBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/CheckBoxBuilder.java index ea99ea8f..eec1bed8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/CheckBoxBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/CheckBoxBuilder.java @@ -114,23 +114,38 @@ public class CheckBoxBuilder implements InputFieldBuilder { @Override protected void setValueToControl(final String value) { - this.control.setSelection(Boolean.parseBoolean(this.initValue)); + if (ViewContext.INVERTED_CHECKBOX_SETTINGS.contains(this.attribute.name)) { + this.control.setSelection(!Boolean.parseBoolean(this.initValue)); + } else { + this.control.setSelection(Boolean.parseBoolean(this.initValue)); + } } @Override public String getValue() { - return this.control.getSelection() - ? Constants.TRUE_STRING - : Constants.FALSE_STRING; + if (ViewContext.INVERTED_CHECKBOX_SETTINGS.contains(this.attribute.name)) { + return this.control.getSelection() + ? Constants.FALSE_STRING + : Constants.TRUE_STRING; + } else { + return this.control.getSelection() + ? Constants.TRUE_STRING + : Constants.FALSE_STRING; + } } @Override public String getReadableValue() { - return this.control.getSelection() - ? "Active" - : "Inactive"; + if (ViewContext.INVERTED_CHECKBOX_SETTINGS.contains(this.attribute.name)) { + return this.control.getSelection() + ? "Inactive" + : "Active"; + } else { + return this.control.getSelection() + ? "Active" + : "Inactive"; + } } - } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ExamConfigurationServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ExamConfigurationServiceImpl.java index b27c63cf..095a0089 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ExamConfigurationServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ExamConfigurationServiceImpl.java @@ -72,6 +72,11 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService { private static final Logger log = LoggerFactory.getLogger(ExamConfigurationServiceImpl.class); + public static final LocTextKey ACTION_ERROR_TITLE = + new LocTextKey("sebserver.configtemplate.action.error.title"); + public static final LocTextKey ACTION_ERROR_MSG = + new LocTextKey("sebserver.configtemplate.action.error.noview.message"); + private final RestService restService; private final JSONMapper jsonMapper; private final WidgetFactory widgetFactory; @@ -254,12 +259,14 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService { final Set selection = action.getMultiSelection(); if (selection != null && !selection.isEmpty()) { selection.forEach(entityKey -> callTemplateAction( + action, ResetTemplateValues.class, parentEntityKey.modelId, entityKey.modelId)); } else { final EntityKey entityKey = action.getEntityKey(); callTemplateAction( + action, ResetTemplateValues.class, parentEntityKey.modelId, entityKey.modelId); @@ -274,12 +281,14 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService { final Set selection = action.getMultiSelection(); if (selection != null && !selection.isEmpty()) { selection.forEach(entityKey -> callTemplateAction( + action, RemoveOrientation.class, parentEntityKey.modelId, entityKey.modelId)); } else { final EntityKey entityKey = action.getEntityKey(); callTemplateAction( + action, RemoveOrientation.class, parentEntityKey.modelId, entityKey.modelId); @@ -294,21 +303,23 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService { final Set selection = action.getMultiSelection(); if (selection != null && !selection.isEmpty()) { selection.forEach(entityKey -> callTemplateAction( + action, AttachDefaultOrientation.class, parentEntityKey.modelId, entityKey.modelId)); } else { final EntityKey entityKey = action.getEntityKey(); callTemplateAction( + action, AttachDefaultOrientation.class, parentEntityKey.modelId, entityKey.modelId); } - return action; } private void callTemplateAction( + final PageAction action, final Class> actionType, final String templateId, final String attributeId) { @@ -317,7 +328,11 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService { .withURIVariable(API.PARAM_PARENT_MODEL_ID, templateId) .withURIVariable(API.PARAM_MODEL_ID, attributeId) .call() - .getOrThrow(); + .onError(error -> { + action.pageContext().publishPageMessage( + ACTION_ERROR_TITLE, + ACTION_ERROR_MSG); + }); } private static final class ValueChangeListenerImpl implements ValueChangeListener { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/PasswordFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/PasswordFieldBuilder.java index 4f73bed8..0b1134d2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/PasswordFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/PasswordFieldBuilder.java @@ -80,6 +80,7 @@ public class PasswordFieldBuilder implements InputFieldBuilder { final PasswordInput passwordInput = new PasswordInput( innerGrid, this.widgetFactory, + attributeNameLocKey, attributeNameLocKey); final GridData passwordInputLD = new GridData(SWT.FILL, SWT.FILL, true, true); passwordInput.setLayoutData(passwordInputLD); @@ -91,6 +92,7 @@ public class PasswordFieldBuilder implements InputFieldBuilder { final PasswordInput confirmInput = new PasswordInput( innerGrid, this.widgetFactory, + confirmNameLocKey, confirmNameLocKey); final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); gridData.verticalIndent = 14; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java index 642dc79a..c65b4623 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java @@ -150,14 +150,25 @@ public class TableFieldBuilder extends AbstractTableFieldBuilder { private void deleteRow(final int selectionIndex) { this.control.remove(selectionIndex); this.values.remove(selectionIndex); + try { + if (this.values.size() > selectionIndex) { + this.control.select(selectionIndex); + } else { + this.control.select(this.values.size() - 1); + } + this.control.showSelection(); + } catch (final Exception e) { + // ignore auto selection error + } // send new values to web-service - this.tableContext.getValueChangeListener() + this.tableContext + .getValueChangeListener() .tableChanged(extractTableValue(this.values)); } private void addRow() { final int index = this.values.size(); - // create new values form default values + // create new values from default values final Map rowValues = this.tableContext.getRowAttributes() .stream() .map(attr -> new TableValue(attr.id, index, attr.defaultValue)) @@ -168,6 +179,12 @@ public class TableFieldBuilder extends AbstractTableFieldBuilder { this.values.add(rowValues); addTableRow(this.values.size() - 1, rowValues); this.control.layout(); + try { + this.control.select(index); + this.control.showSelection(); + } catch (final Exception e) { + // ignore auto selection error + } // send new values to web-service this.tableContext.getValueChangeListener() .tableChanged(extractTableValue(this.values)); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ViewContext.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ViewContext.java index dd34d845..29681987 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ViewContext.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/ViewContext.java @@ -8,10 +8,13 @@ package ch.ethz.seb.sebserver.gui.service.examconfig.impl; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.Function; import org.slf4j.Logger; @@ -30,6 +33,10 @@ public final class ViewContext { private static final Logger log = LoggerFactory.getLogger(ViewContext.class); + /** Defines a list of checkbox fields that are inverted on the display of SEB settings */ + public static final Set INVERTED_CHECKBOX_SETTINGS = new HashSet<>(Arrays.asList( + "enableSebBrowser")); + private final Configuration configuration; private final View view; private final Function viewContextSupplier; @@ -274,6 +281,16 @@ public final class ViewContext { } } + public void putValue(final String name, final String value) { + try { + final ConfigurationAttribute attributeByName = getAttributeByName(name); + final InputField inputField = this.inputFieldMapping.get(attributeByName.id); + inputField.initValue(value, 0); + } catch (final Exception e) { + log.error("Failed to put attribute value: {} : {}, cause {}", name, value, e.getMessage()); + } + } + public void setValue(final String name, final String value) { try { final ConfigurationAttribute attributeByName = getAttributeByName(name); @@ -289,7 +306,6 @@ public final class ViewContext { } catch (final Exception e) { log.error("Failed to set attribute value: {} : {}, cause {}", name, value, e.getMessage()); } - } void setValuesToInputFields(final Collection values) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/I18nSupport.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/I18nSupport.java index fe7b70a4..80a35e0c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/I18nSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/I18nSupport.java @@ -43,7 +43,7 @@ public interface I18nSupport { * This uses the date-format defined by either the attribute 'sebserver.gui.date.display format' * or the Constants.DEFAULT_DISPLAY_DATE_FORMAT * - * Adds time-zone offset information if the currents user time-zone is different form UTC + * Adds time-zone offset information if the currents user time-zone is different from UTC * * @param date the DateTime instance * @return date formatted date String to display */ @@ -53,7 +53,7 @@ public interface I18nSupport { * This uses the date-format defined by either the attribute 'sebserver.gui.date.display format' * or the Constants.DEFAULT_DISPLAY_DATE_FORMAT * - * Adds time-zone offset information if the currents user time-zone is different form UTC + * Adds time-zone offset information if the currents user time-zone is different from UTC * * @param date the DateTime instance * @return date formatted date String to display */ @@ -65,7 +65,7 @@ public interface I18nSupport { * This uses the date-format defined by either the attribute 'sebserver.gui.date.display format' * or the Constants.DEFAULT_DISPLAY_DATE_FORMAT * - * Adds time-zone information if the currents user time-zone is different form UTC + * Adds time-zone information if the currents user time-zone is different from UTC * * @param timestamp the unix-timestamp in milliseconds * @return date formatted date String to display */ @@ -77,7 +77,7 @@ public interface I18nSupport { * This uses the date-format defined by either the attribute 'sebserver.gui.datetime.display format' * or the Constants.DEFAULT_DISPLAY_DATE_TIME_FORMAT * - * Adds time-zone information if the currents user time-zone is different form UTC + * Adds time-zone information if the currents user time-zone is different from UTC * * @param date the DateTime instance * @return date formatted date time String to display */ @@ -87,7 +87,7 @@ public interface I18nSupport { * This uses the date-format defined by either the attribute 'sebserver.gui.datetime.display format' * or the Constants.DEFAULT_DISPLAY_DATE_TIME_FORMAT * - * Adds time-zone information if the currents user time-zone is different form UTC + * Adds time-zone information if the currents user time-zone is different from UTC * * @param timestamp the unix-timestamp in milliseconds * @return date formatted date time String to display */ @@ -99,7 +99,7 @@ public interface I18nSupport { * This uses the date-format defined by either the attribute 'sebserver.gui.time.display format' * or the Constants.DEFAULT_DISPLAY_TIME_FORMAT * - * Adds time-zone information if the currents user time-zone is different form UTC + * Adds time-zone information if the currents user time-zone is different from UTC * * @param date the DateTime instance * @return date formatted time String to display */ @@ -109,7 +109,7 @@ public interface I18nSupport { * This uses the date-format defined by either the attribute 'sebserver.gui.time.display format' * or the Constants.DEFAULT_DISPLAY_TIME_FORMAT * - * Adds time-zone information if the currents user time-zone is different form UTC + * Adds time-zone information if the currents user time-zone is different from UTC * * @param timestamp the unix-timestamp in milliseconds * @return date formatted time String to display */ diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/AbstractBatchActionWizard.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/AbstractBatchActionWizard.java new file mode 100644 index 00000000..253e1875 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/AbstractBatchActionWizard.java @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2022 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.page; + +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.swt.widgets.Composite; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType; +import ch.ethz.seb.sebserver.gbl.model.BatchAction; +import ch.ethz.seb.sebserver.gbl.model.Domain.BATCH_ACTION; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; +import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +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.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputWizard; +import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputWizard.WizardAction; +import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputWizard.WizardPage; +import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; +import ch.ethz.seb.sebserver.gui.service.push.ServerPushContext; +import ch.ethz.seb.sebserver.gui.service.push.ServerPushService; +import ch.ethz.seb.sebserver.gui.service.push.UpdateErrorHandler; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.batch.DoBatchAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.batch.GetBatchAction; + +public abstract class AbstractBatchActionWizard { + + protected final String BATCH_ACTION_PAGE_NAME = "BATCH_ACTION_PAGE"; + protected final String BATCH_ACTION_RESULT_PAGE_NAME = "BATCH_ACTION_RESULT_PAGE"; + protected static final String SELECTED_OBJECTS_NAME = "FORM_SELECTED_OBJECTS"; + protected static final String FORM_FAILURE_NAME = "FAILURE"; + protected static final String FORM_SUCCESS_NAME = "SUCCESS"; + protected static final String FORM_PROGRESS_NAME = "PROGRESS"; + + private final static LocTextKey FORM_SELECTED_OBJECTS = + new LocTextKey("sebserver.overall.batchaction.selected"); + + protected final PageService pageService; + protected final ServerPushService serverPushService; + + protected AbstractBatchActionWizard( + final PageService pageService, + final ServerPushService serverPushService) { + + this.pageService = pageService; + this.serverPushService = serverPushService; + } + + protected abstract LocTextKey getTitle(); + + protected abstract LocTextKey getBatchActionInfo(); + + protected abstract LocTextKey getBatchActionTitle(); + + protected abstract BatchActionType getBatchActionType(); + + protected abstract Supplier createResultPageSupplier( + final PageContext pageContext, + final FormHandle formHandle); + + protected abstract void extendBatchActionRequest( + PageContext pageContext, + RestCall.RestCallBuilder batchActionRequestBuilder); + + protected abstract FormBuilder buildSpecificFormFields( + final PageContext formContext, + final FormBuilder formHead, + final boolean readonly); + + public Function popupCreationFunction(final PageContext pageContext) { + + return action -> { + final Set multiSelection = action.getMultiSelection(); + if (multiSelection == null || multiSelection.isEmpty()) { + return action; + } + + final ModalInputWizard wizard = + new ModalInputWizard( + action.pageContext().getParent().getShell(), + this.pageService.getWidgetFactory()) + .setVeryLargeDialogWidth(); + + final WizardPage page1 = new WizardPage<>( + this.BATCH_ACTION_PAGE_NAME, + true, + (prefPageContext, content) -> composeFormPage(content, pageContext, multiSelection), + new WizardAction<>(getBatchActionTitle(), this.BATCH_ACTION_RESULT_PAGE_NAME)); + + final WizardPage page2 = new WizardPage<>( + this.BATCH_ACTION_RESULT_PAGE_NAME, + false, + (prefPageContext, content) -> composeResultPage(content, prefPageContext, multiSelection)); + + wizard.open(getTitle(), Utils.EMPTY_EXECUTION, page1, page2); + + return action; + }; + } + + public Supplier composeFormPage( + final Composite parent, + final PageContext pageContext, + final Set multiSelection) { + + final PageService pageService = this.pageService; + final PageContext formContext = pageContext.copyOf(parent); + + final LocTextKey info = getBatchActionInfo(); + if (info != null) { + pageService.getWidgetFactory().labelLocalized(parent, info, true); + } + + final FormHandle formHandle = getFormHeadBuilder( + pageService, + formContext, + multiSelection, + false) + .build(); + + return createResultPageSupplier(pageContext, formHandle); + } + + public Supplier composeResultPage( + final Composite parent, + final PageContext pageContext, + final Set multiSelection) { + + try { + + final String ids = StringUtils.join( + multiSelection.stream().map(key -> key.modelId).collect(Collectors.toList()), + Constants.LIST_SEPARATOR_CHAR); + + final RestCall.RestCallBuilder batchActionRequestBuilder = this.pageService + .getRestService() + .getBuilder(DoBatchAction.class) + .withFormParam(BATCH_ACTION.ATTR_ACTION_TYPE, getBatchActionType().name()) + .withFormParam(BATCH_ACTION.ATTR_SOURCE_IDS, ids); + + extendBatchActionRequest(pageContext, batchActionRequestBuilder); + + final BatchAction batchAction = batchActionRequestBuilder + .call() + .getOrThrow(); + + final ProgressUpdate progressUpdate = new ProgressUpdate(batchAction.getModelId()); + final PageContext formContext = pageContext.copyOf(parent); + + final LocTextKey info = getBatchActionInfo(); + if (info != null) { + this.pageService.getWidgetFactory().labelLocalized(parent, info, true); + } + + final FormHandle formHandle = getFormHeadBuilder( + this.pageService, + formContext, + multiSelection, + true) + .build(); + + this.serverPushService.runServerPush( + new ServerPushContext( + parent, + context -> !progressUpdate.isFinished(), + new UpdateErrorHandler( + this.pageService, + formContext)), + 1000, + context -> progressUpdate.update(), + context -> updateGUI(context, formContext, progressUpdate, formHandle.getForm())); + } catch (final Exception e) { + pageContext.notifyUnexpectedError(e); + throw e; + } + + return () -> pageContext; + } + + protected void updateGUI( + final ServerPushContext context, + final PageContext formContext, + final ProgressUpdate progressCall, + final Form form) { + + if (!progressCall.isFinished()) { + form.setFieldValue( + FORM_PROGRESS_NAME, + progressCall.batchAction.getProgress() + " %"); + } else { + form.setFieldValue( + FORM_PROGRESS_NAME, + "100 %"); + } + + form.setFieldValue( + FORM_SUCCESS_NAME, + String.valueOf(progressCall.batchAction.successful.size())); + form.setFieldValue( + FORM_FAILURE_NAME, + String.valueOf(progressCall.batchAction.failures.size())); + + formContext.getParent().layout(true, true); + + this.pageService.executePageAction(this.pageService.pageActionBuilder(formContext) + .newAction(ActionDefinition.SEB_EXAM_CONFIG_LIST) + .create()); + } + + protected FormBuilder getFormHeadBuilder( + final PageService pageService, + final PageContext formContext, + final Set multiSelection, + final boolean readonly) { + + final FormBuilder formBuilder = pageService + .formBuilder(formContext) + .addField(FormBuilder.text( + SELECTED_OBJECTS_NAME, + FORM_SELECTED_OBJECTS, + String.valueOf(multiSelection.size())) + .readonly(true)); + + buildSpecificFormFields(formContext, formBuilder, readonly); + return buildProgressFields(formBuilder, readonly); + } + + protected FormBuilder buildProgressFields(final FormBuilder formHead, final boolean readonly) { + return formHead + .addField(FormBuilder.text( + FORM_PROGRESS_NAME, + new LocTextKey("Progress"), + "0 %") + .readonly(true).visibleIf(!readonly)) + + .addField(FormBuilder.text( + FORM_SUCCESS_NAME, + new LocTextKey("Success"), + "0") + .asNumber() + .readonly(true).visibleIf(!readonly)) + + .addField(FormBuilder.text( + FORM_FAILURE_NAME, + new LocTextKey("Failures"), + "0") + .asNumber() + .readonly(true).visibleIf(!readonly)); + } + + private final class ProgressUpdate { + + final RestCall.RestCallBuilder progressCall; + private BatchAction batchAction = null; + + ProgressUpdate(final String modelId) { + this.progressCall = AbstractBatchActionWizard.this.pageService + .getRestService() + .getBuilder(GetBatchAction.class) + .withURIVariable(API.PARAM_MODEL_ID, modelId); + } + + void update() { + this.batchAction = this.progressCall.call().getOrThrow(); + } + + boolean isFinished() { + return this.batchAction != null && this.batchAction.isFinished(); + } + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageService.java index 4cee4db2..bac021ac 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageService.java @@ -8,16 +8,13 @@ package ch.ethz.seb.sebserver.gui.service.page; -import java.util.Arrays; import java.util.Collection; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.BooleanSupplier; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import java.util.stream.Collectors; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; @@ -35,6 +32,7 @@ import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.model.Activatable; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.ModelIdAware; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Tuple; @@ -61,6 +59,9 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; * with forms and tables as well as dealing with page actions */ public interface PageService { + LocTextKey MESSAGE_NO_MULTISELECTION = + new LocTextKey("sebserver.overall.action.toomanyselection"); + enum FormTooltipMode { RIGHT, INPUT, @@ -153,31 +154,34 @@ public interface PageService { return this.activationToggleActionFunction(table, noSelectionText, null); } - /** Get a message supplier to notify deactivation dependencies to the user for all given entities - * - * @param entities Set of entities to collect the dependencies for - * @return a message supplier to notify deactivation dependencies to the user */ - Supplier confirmDeactivation(final Set entities); +// /** Get a message supplier to notify deactivation dependencies to the user for all given entities +// * +// * @param entities Set of entities to collect the dependencies for +// * @return a message supplier to notify deactivation dependencies to the user */ +// Supplier confirmDeactivation(final Set keys); /** Get a message supplier to notify deactivation dependencies to the user for given entity * * @param entity the entity instance * @return a message supplier to notify deactivation dependencies to the user */ - default Supplier confirmDeactivation(final T entity) { - return confirmDeactivation(new HashSet<>(Arrays.asList(entity))); - } + Supplier confirmDeactivation(final T entity); /** Get a message supplier to notify deactivation dependencies to the user for given entity table selection * * @param table the entity table * @return a message supplier to notify deactivation dependencies to the user */ default Supplier confirmDeactivation(final EntityTable table) { - return () -> confirmDeactivation(table - .getSelectedROWData() - .stream() - .filter(entity -> entity.isActive()) // NOTE: Activatable::isActive leads to an error here!? - .collect(Collectors.toSet())) - .get(); + return () -> { + final Set multiSelection = table.getMultiSelection(); + if (multiSelection.size() > 1) { + throw new PageMessageException(MESSAGE_NO_MULTISELECTION); + } + final T entity = table.getSingleSelectedROWData(); + if (!entity.isActive()) { + return null; + } + return confirmDeactivation(entity).get(); + }; } /** Use this to get an action activation publisher that processes the action activation. @@ -200,12 +204,12 @@ public interface PageService { * @param pageContext the current PageContext * @param actionDefinitions list of action definitions that activity should be toggled on table selection * @return the selection publisher that handles this defines action activation on table selection */ - default Consumer> getSelectionPublisher( + default Consumer> getSelectionPublisher( final PageContext pageContext, final ActionDefinition... actionDefinitions) { - return rows -> firePageEvent( - new ActionActivationEvent(!rows.isEmpty(), actionDefinitions), + return table -> firePageEvent( + new ActionActivationEvent(table.hasSelection(), actionDefinitions), pageContext); } @@ -221,15 +225,15 @@ public interface PageService { * @param pageContext the current PageContext * @param actionDefinitions list of action definitions that activity should be toggled on table selection * @return the selection publisher that handles this defines action activation on table selection */ - default Consumer> getSelectionPublisher( + default Consumer> getSelectionPublisher( final ActionDefinition toggle, final ActionDefinition activate, final ActionDefinition deactivate, final PageContext pageContext, final ActionDefinition... actionDefinitions) { - return rows -> { - + return table -> { + final Set rows = table.getPageSelectionData(); if (!rows.isEmpty()) { firePageEvent( new ActionActivationEvent( @@ -321,7 +325,7 @@ public interface PageService { * @param apiCall the SEB Server API RestCall that feeds the table with data * @param the type of the Entity of the table * @return TableBuilder of specified type */ - default TableBuilder entityTableBuilder(final RestCall> apiCall) { + default TableBuilder entityTableBuilder(final RestCall> apiCall) { return entityTableBuilder(apiCall.getClass().getSimpleName(), apiCall); } @@ -331,13 +335,14 @@ public interface PageService { * @param apiCall the SEB Server API RestCall that feeds the table with data * @param the type of the Entity of the table * @return TableBuilder of specified type */ - TableBuilder entityTableBuilder( + TableBuilder entityTableBuilder( String name, RestCall> apiCall); - TableBuilder staticListTableBuilder(final List staticList, EntityType entityType); + TableBuilder staticListTableBuilder(final List staticList, EntityType entityType); - TableBuilder remoteListTableBuilder(RestCall> apiCall, EntityType entityType); + TableBuilder remoteListTableBuilder(RestCall> apiCall, + EntityType entityType); /** Get a new PageActionBuilder for a given PageContext. * diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/DefaultPageLayout.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/DefaultPageLayout.java index 8c67a1e2..53e5f7fd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/DefaultPageLayout.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/DefaultPageLayout.java @@ -12,7 +12,6 @@ import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import org.apache.commons.codec.binary.Base64InputStream; -import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.client.service.UrlLauncher; @@ -31,11 +30,10 @@ import org.eclipse.swt.widgets.MessageBox; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; -import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; -import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gui.GuiServiceInfo; import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService; @@ -70,19 +68,17 @@ public class DefaultPageLayout implements TemplateComposer { private final PolyglotPageService polyglotPageService; private final AuthorizationContextHolder authorizationContextHolder; private final PageService pageService; - private final String sebServerVersion; - private final boolean multilingual; + private final GuiServiceInfo guiServiceInfo; public DefaultPageLayout( final PageService pageService, - final Environment environment) { + final GuiServiceInfo guiServiceInfo) { this.widgetFactory = pageService.getWidgetFactory(); this.polyglotPageService = pageService.getPolyglotPageService(); this.authorizationContextHolder = pageService.getAuthorizationContextHolder(); this.pageService = pageService; - this.sebServerVersion = environment.getProperty("sebserver.version", Constants.EMPTY_NOTE); - this.multilingual = BooleanUtils.toBoolean(environment.getProperty("sebserver.gui.multilingual", "false")); + this.guiServiceInfo = guiServiceInfo; } @Override @@ -198,7 +194,7 @@ public class DefaultPageLayout implements TemplateComposer { rowLayout.marginRight = 70; langSupport.setLayout(rowLayout); - if (this.multilingual) { + if (this.guiServiceInfo.isMultilingualGUI()) { this.polyglotPageService.createLanguageSelector(pageContext.copyOf(langSupport)); } } @@ -332,7 +328,7 @@ public class DefaultPageLayout implements TemplateComposer { this.widgetFactory.labelLocalized( footerRight, CustomVariant.FOOTER, - new LocTextKey("sebserver.overall.version", this.sebServerVersion)); + new LocTextKey("sebserver.overall.version", this.guiServiceInfo.getSebServerVersion())); } private void loadInstitutionalLogo(final PageContext pageContext, final Composite logo) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/JitsiMeetProctoringView.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/JitsiMeetProctoringView.java index 8ffae1b5..2c58f42d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/JitsiMeetProctoringView.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/JitsiMeetProctoringView.java @@ -32,7 +32,7 @@ import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gui.GuiServiceInfo; 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.remote.webservice.api.exam.GetProctoringSettings; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings; import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService; import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @@ -69,7 +69,7 @@ public class JitsiMeetProctoringView extends AbstractProctoringView { .getProctoringGUIService(); final ProctoringServiceSettings proctoringSettings = this.pageService .getRestService() - .getBuilder(GetProctoringSettings.class) + .getBuilder(GetExamProctoringSettings.class) .withURIVariable(API.PARAM_MODEL_ID, proctoringWindowData.examId) .call() .onError(error -> log.error("Failed to get ProctoringServiceSettings", error)) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModalInputWizard.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModalInputWizard.java index b96b61f0..2645356f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModalInputWizard.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ModalInputWizard.java @@ -171,7 +171,9 @@ public class ModalInputWizard extends Dialog { } } - final Button cancel = this.widgetFactory.buttonLocalized(actionsComp, ModalInputDialog.CANCEL_TEXT_KEY); + final Button cancel = this.widgetFactory.buttonLocalized( + actionsComp, + (page.actions.isEmpty()) ? ModalInputDialog.OK_TEXT_KEY : ModalInputDialog.CANCEL_TEXT_KEY); final RowData data = new RowData(); data.width = this.buttonWidth; cancel.setLayoutData(data); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageAction.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageAction.java index 76cd4929..1c78953e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageAction.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageAction.java @@ -167,24 +167,24 @@ public final class PageAction { void applyAction(final Consumer> callback) { if (this.confirm != null) { - // if selection is needed, check selection fist, before confirm dialog - if (this.selectionSupplier != null) { - try { + try { + // if selection is needed, check selection fist, before confirm dialog + if (this.selectionSupplier != null) { getMultiSelection(); - } catch (final PageMessageException pme) { - PageAction.this.pageContext.publishPageMessage(pme); - return; } - } - final LocTextKey confirmMessage = this.confirm.apply(this); - if (confirmMessage != null) { - this.pageContext.applyConfirmDialog(confirmMessage, - confirm -> callback.accept((confirm) - ? exec().onError(error -> this.pageContext.notifyUnexpectedError(error)) - : Result.ofRuntimeError("Confirm denied"))); - } else { - callback.accept(exec()); + final LocTextKey confirmMessage = this.confirm.apply(this); + if (confirmMessage != null) { + this.pageContext.applyConfirmDialog(confirmMessage, + confirm -> callback.accept((confirm) + ? exec().onError(error -> this.pageContext.notifyUnexpectedError(error)) + : Result.ofRuntimeError("Confirm denied"))); + } else { + callback.accept(exec()); + } + } catch (final PageMessageException pme) { + PageAction.this.pageContext.publishPageMessage(pme); + return; } } else { callback.accept(exec()); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageServiceImpl.java index 1d87f60e..8faf5b3b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageServiceImpl.java @@ -17,7 +17,6 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import java.util.stream.Stream; import javax.servlet.http.HttpSession; @@ -35,6 +34,7 @@ import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.model.Activatable; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.ModelIdAware; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.util.Cryptor; @@ -69,13 +69,11 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @GuiProfile public class PageServiceImpl implements PageService { - private static final LocTextKey CONFIRM_DEACTIVATION_NO_DEP_KEY = - new LocTextKey("sebserver.dialog.confirm.deactivation.noDependencies"); - - private static final String CONFIRM_DEACTIVATION_KEY = "sebserver.dialog.confirm.deactivation"; - private static final Logger log = LoggerFactory.getLogger(PageServiceImpl.class); + private static final LocTextKey CONFIRM_DEACTIVATION_NO_DEP_KEY = + new LocTextKey("sebserver.dialog.confirm.deactivation.noDependencies"); + private static final String CONFIRM_DEACTIVATION_KEY = "sebserver.dialog.confirm.deactivation"; private static final LocTextKey MSG_GO_AWAY_FROM_EDIT = new LocTextKey("sebserver.overall.action.goAwayFromEditPageConfirm"); @@ -167,7 +165,6 @@ public class PageServiceImpl implements PageService { @Override public FormTooltipMode getFormTooltipMode() { - // TODO make this configurable return FormTooltipMode.INPUT; } @@ -219,31 +216,19 @@ public class PageServiceImpl implements PageService { } @Override - public Supplier confirmDeactivation(final Set entities) { + public Supplier confirmDeactivation(final T entity) { final RestService restService = this.resourceService.getRestService(); return () -> { - if (entities == null || entities.isEmpty()) { - return null; - } - try { - final int dependencies = (int) entities.stream() - .flatMap(entity -> { - final RestCall>.RestCallBuilder builder = - restService.> getBuilder( - entity.entityType(), - CallType.GET_DEPENDENCIES); + final int dependencies = restService.> getBuilder( + entity.entityType(), + CallType.GET_DEPENDENCIES) + .withURIVariable(API.PARAM_MODEL_ID, String.valueOf(entity.getModelId())) + .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.DEACTIVATE.name()) + .call() + .getOrThrow() + .size(); - if (builder != null) { - return builder - .withURIVariable(API.PARAM_MODEL_ID, String.valueOf(entity.getModelId())) - .withQueryParam(API.PARAM_BULK_ACTION_TYPE, BulkActionType.DEACTIVATE.name()) - .call() - .getOrThrow().stream(); - } else { - return Stream.empty(); - } - }).count(); if (dependencies > 0) { return new LocTextKey(CONFIRM_DEACTIVATION_KEY, String.valueOf(dependencies)); } else { @@ -264,43 +249,47 @@ public class PageServiceImpl implements PageService { final Function testBeforeActivation) { return action -> { - final Set selectedROWData = table.getSelectedROWData(); - if (selectedROWData == null || selectedROWData.isEmpty()) { + final Set multiSelection = table.getMultiSelection(); + if (multiSelection == null || multiSelection.isEmpty()) { throw new PageMessageException(noSelectionText); } + if (multiSelection.size() > 1) { + throw new PageMessageException(MESSAGE_NO_MULTISELECTION); + } final RestService restService = this.resourceService.getRestService(); final EntityType entityType = table.getEntityType(); - final Collection errors = new ArrayList<>(); - for (final T entity : selectedROWData) { + final T singleSelection = table.getSingleSelectedROWData(); + if (singleSelection == null) { + throw new PageMessageException(noSelectionText); + } - if (!entity.isActive()) { - final RestCall.RestCallBuilder restCallBuilder = restService. getBuilder( - entityType, - CallType.ACTIVATION_ACTIVATE) - .withURIVariable(API.PARAM_MODEL_ID, entity.getModelId()); - if (testBeforeActivation != null) { - try { - action.withEntityKey(entity.getEntityKey()); - testBeforeActivation.apply(action); - restCallBuilder - .call() - .onError(errors::add); - } catch (final Exception e) { - errors.add(e); - } - } else { + if (!singleSelection.isActive()) { + final RestCall.RestCallBuilder restCallBuilder = restService. getBuilder( + entityType, + CallType.ACTIVATION_ACTIVATE) + .withURIVariable(API.PARAM_MODEL_ID, singleSelection.getModelId()); + if (testBeforeActivation != null) { + try { + action.withEntityKey(singleSelection.getEntityKey()); + testBeforeActivation.apply(action); restCallBuilder .call() .onError(errors::add); + } catch (final Exception e) { + errors.add(e); } } else { - restService. getBuilder(entityType, CallType.ACTIVATION_DEACTIVATE) - .withURIVariable(API.PARAM_MODEL_ID, entity.getModelId()) + restCallBuilder .call() .onError(errors::add); } + } else { + restService. getBuilder(entityType, CallType.ACTIVATION_DEACTIVATE) + .withURIVariable(API.PARAM_MODEL_ID, singleSelection.getModelId()) + .call() + .onError(errors::add); } if (!errors.isEmpty()) { @@ -365,7 +354,7 @@ public class PageServiceImpl implements PageService { } @Override - public TableBuilder entityTableBuilder( + public TableBuilder entityTableBuilder( final String name, final RestCall> apiCall) { @@ -373,7 +362,7 @@ public class PageServiceImpl implements PageService { } @Override - public TableBuilder staticListTableBuilder( + public TableBuilder staticListTableBuilder( final List staticList, final EntityType entityType) { @@ -385,7 +374,7 @@ public class PageServiceImpl implements PageService { } @Override - public TableBuilder remoteListTableBuilder( + public TableBuilder remoteListTableBuilder( final RestCall> apiCall, final EntityType entityType) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ZoomProctoringView.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ZoomProctoringView.java index 90013704..33cf3cee 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ZoomProctoringView.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ZoomProctoringView.java @@ -32,7 +32,7 @@ import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gui.GuiServiceInfo; 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.remote.webservice.api.exam.GetProctoringSettings; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings; import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService; import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @@ -69,7 +69,7 @@ public class ZoomProctoringView extends AbstractProctoringView { .getProctoringGUIService(); final ProctoringServiceSettings proctoringSettings = this.pageService .getRestService() - .getBuilder(GetProctoringSettings.class) + .getBuilder(GetExamProctoringSettings.class) .withURIVariable(API.PARAM_MODEL_ID, proctoringWindowData.examId) .call() .onError(error -> log.error("Failed to get ProctoringServiceSettings", error)) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/push/UpdateErrorHandler.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/push/UpdateErrorHandler.java index 45c45a91..c453c9bc 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/push/UpdateErrorHandler.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/push/UpdateErrorHandler.java @@ -63,7 +63,7 @@ public final class UpdateErrorHandler implements Function { @Override public Boolean apply(final Exception error) { this.errors++; - log.error("Failed to update server push: {}", error.getMessage()); + log.error("Failed to update server push: {}", error.getMessage(), error); if (this.errors > 5) { checkUserSession(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/batch/DoBatchAction.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/batch/DoBatchAction.java new file mode 100644 index 00000000..860db1f8 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/batch/DoBatchAction.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 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.batch; + +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.BatchAction; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class DoBatchAction extends RestCall { + + public DoBatchAction() { + super(new TypeKey<>( + CallType.NEW, + EntityType.BATCH_ACTION, + new TypeReference() { + }), + HttpMethod.POST, + MediaType.APPLICATION_FORM_URLENCODED, + API.BATCH_ACTION_ENDPOINT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/batch/GetBatchAction.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/batch/GetBatchAction.java new file mode 100644 index 00000000..b06e895e --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/batch/GetBatchAction.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 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.batch; + +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.BatchAction; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class GetBatchAction extends RestCall { + + public GetBatchAction() { + super(new TypeKey<>( + CallType.GET_SINGLE, + EntityType.BATCH_ACTION, + new TypeReference() { + }), + HttpMethod.GET, + MediaType.APPLICATION_FORM_URLENCODED, + API.BATCH_ACTION_ENDPOINT + + API.MODEL_ID_VAR_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/batch/GetBatchActionPage.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/batch/GetBatchActionPage.java new file mode 100644 index 00000000..8d156ff6 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/batch/GetBatchActionPage.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 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.batch; + +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.BatchAction; +import ch.ethz.seb.sebserver.gbl.model.Page; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class GetBatchActionPage extends RestCall> { + + public GetBatchActionPage() { + super(new TypeKey<>( + CallType.GET_PAGE, + EntityType.BATCH_ACTION, + new TypeReference>() { + }), + HttpMethod.GET, + MediaType.APPLICATION_FORM_URLENCODED, + API.BATCH_ACTION_ENDPOINT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/ArchiveExam.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/ArchiveExam.java new file mode 100644 index 00000000..1783cb63 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/ArchiveExam.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 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 ArchiveExam extends RestCall { + + public ArchiveExam() { + super(new TypeKey<>( + CallType.SAVE, + EntityType.EXAM, + new TypeReference() { + }), + HttpMethod.PATCH, + MediaType.APPLICATION_JSON, + API.EXAM_ADMINISTRATION_ENDPOINT + + API.MODEL_ID_VAR_PATH_SEGMENT + + API.EXAM_ADMINISTRATION_ARCHIVE_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetProctoringSettings.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamProctoringSettings.java similarity index 89% rename from src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetProctoringSettings.java rename to src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamProctoringSettings.java index 25c836a7..2f1a3e07 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetProctoringSettings.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamProctoringSettings.java @@ -24,9 +24,9 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; @Lazy @Component @GuiProfile -public class GetProctoringSettings extends RestCall { +public class GetExamProctoringSettings extends RestCall { - public GetProctoringSettings() { + public GetExamProctoringSettings() { super(new TypeKey<>( CallType.GET_SINGLE, EntityType.EXAM_PROCTOR_DATA, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamTemplateProctoringSettings.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamTemplateProctoringSettings.java new file mode 100644 index 00000000..d59937fe --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/GetExamTemplateProctoringSettings.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 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.ProctoringServiceSettings; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class GetExamTemplateProctoringSettings extends RestCall { + + public GetExamTemplateProctoringSettings() { + super(new TypeKey<>( + CallType.GET_SINGLE, + EntityType.EXAM_PROCTOR_DATA, + new TypeReference() { + }), + HttpMethod.GET, + MediaType.APPLICATION_JSON, + API.EXAM_TEMPLATE_ENDPOINT + + API.MODEL_ID_VAR_PATH_SEGMENT + + API.EXAM_ADMINISTRATION_PROCTORING_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/SaveProctoringSettings.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/SaveExamProctoringSettings.java similarity index 80% rename from src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/SaveProctoringSettings.java rename to src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/SaveExamProctoringSettings.java index dbacc253..8a3c5bfb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/SaveProctoringSettings.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/SaveExamProctoringSettings.java @@ -17,20 +17,20 @@ 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.model.exam.ProctoringServiceSettings; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; @Lazy @Component @GuiProfile -public class SaveProctoringSettings extends RestCall { +public class SaveExamProctoringSettings extends RestCall { - public SaveProctoringSettings() { + public SaveExamProctoringSettings() { super(new TypeKey<>( CallType.SAVE, EntityType.EXAM_PROCTOR_DATA, - new TypeReference() { + new TypeReference() { }), HttpMethod.POST, MediaType.APPLICATION_JSON, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/SaveExamTemplateProctoringSettings.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/SaveExamTemplateProctoringSettings.java new file mode 100644 index 00000000..3f41700c --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/exam/SaveExamTemplateProctoringSettings.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 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.ProctoringServiceSettings; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class SaveExamTemplateProctoringSettings extends RestCall { + + public SaveExamTemplateProctoringSettings() { + super(new TypeKey<>( + CallType.SAVE, + EntityType.EXAM_PROCTOR_DATA, + new TypeReference() { + }), + HttpMethod.POST, + MediaType.APPLICATION_JSON, + API.EXAM_TEMPLATE_ENDPOINT + + API.MODEL_ID_VAR_PATH_SEGMENT + + API.EXAM_ADMINISTRATION_PROCTORING_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/institution/DeleteInstitution.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/institution/DeleteInstitution.java new file mode 100644 index 00000000..89f90ade --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/institution/DeleteInstitution.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 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.institution; + +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 DeleteInstitution extends RestCall { + + public DeleteInstitution() { + super(new TypeKey<>( + CallType.DELETE, + EntityType.EXAM, + new TypeReference() { + }), + HttpMethod.DELETE, + MediaType.APPLICATION_FORM_URLENCODED, + API.INSTITUTION_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/lmssetup/DeleteLmsSetup.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/lmssetup/DeleteLmsSetup.java new file mode 100644 index 00000000..10ef99e0 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/lmssetup/DeleteLmsSetup.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 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 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 DeleteLmsSetup extends RestCall { + + public DeleteLmsSetup() { + super(new TypeKey<>( + CallType.DELETE, + EntityType.EXAM, + new TypeReference() { + }), + HttpMethod.DELETE, + MediaType.APPLICATION_FORM_URLENCODED, + API.LMS_SETUP_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/clientconfig/DeleteClientConfig.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/clientconfig/DeleteClientConfig.java new file mode 100644 index 00000000..ec384b6c --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/clientconfig/DeleteClientConfig.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 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.clientconfig; + +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 DeleteClientConfig extends RestCall { + + public DeleteClientConfig() { + super(new TypeKey<>( + CallType.DELETE, + EntityType.EXAM, + new TypeReference() { + }), + HttpMethod.DELETE, + MediaType.APPLICATION_FORM_URLENCODED, + API.SEB_CLIENT_CONFIG_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/clientconfig/GetClientCredentials.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/clientconfig/GetClientCredentials.java new file mode 100644 index 00000000..1eeb6ee3 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/clientconfig/GetClientCredentials.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 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.clientconfig; + +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.client.ClientCredentials; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class GetClientCredentials extends RestCall { + + public GetClientCredentials() { + super(new TypeKey<>( + CallType.GET_SINGLE, + null, + new TypeReference() { + }), + HttpMethod.GET, + MediaType.APPLICATION_FORM_URLENCODED, + API.SEB_CLIENT_CONFIG_ENDPOINT + + API.SEB_CLIENT_CONFIG_CREDENTIALS_PATH_SEGMENT + + API.MODEL_ID_VAR_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/ResetToTemplateSettings.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/ResetToTemplateSettings.java new file mode 100644 index 00000000..289a1f88 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/seb/examconfig/ResetToTemplateSettings.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 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.ConfigurationNode; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class ResetToTemplateSettings extends RestCall { + + public ResetToTemplateSettings() { + super(new TypeKey<>( + CallType.SAVE, + EntityType.CONFIGURATION_NODE, + new TypeReference() { + }), + HttpMethod.PATCH, + MediaType.APPLICATION_FORM_URLENCODED, + API.CONFIGURATION_NODE_ENDPOINT + + API.MODEL_ID_VAR_PATH_SEGMENT + + API.CONFIGURATION_RESET_TO_TEMPLATE_PATH_SEGMENT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetFinishedExamClientConnection.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetFinishedExamClientConnection.java new file mode 100644 index 00000000..bcf12c43 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetFinishedExamClientConnection.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 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.session; + +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.session.ClientConnectionData; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class GetFinishedExamClientConnection extends RestCall { + + public GetFinishedExamClientConnection() { + super(new TypeKey<>( + CallType.GET_SINGLE, + EntityType.CLIENT_CONNECTION, + new TypeReference() { + }), + HttpMethod.GET, + MediaType.APPLICATION_FORM_URLENCODED, + API.SEB_CLIENT_CONNECTION_ENDPOINT + + API.SEB_CLIENT_CONNECTION_DATA_ENDPOINT + + API.MODEL_ID_VAR_PATH_SEGMENT); + } +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetFinishedExamClientConnectionPage.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetFinishedExamClientConnectionPage.java new file mode 100644 index 00000000..8d46b940 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetFinishedExamClientConnectionPage.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 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.session; + +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.session.ClientConnectionData; +import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; + +@Lazy +@Component +@GuiProfile +public class GetFinishedExamClientConnectionPage extends RestCall> { + + public GetFinishedExamClientConnectionPage() { + super(new TypeKey<>( + CallType.GET_PAGE, + EntityType.CLIENT_CONNECTION, + new TypeReference>() { + }), + HttpMethod.GET, + MediaType.APPLICATION_FORM_URLENCODED, + API.SEB_CLIENT_CONNECTION_ENDPOINT + API.SEB_CLIENT_CONNECTION_DATA_ENDPOINT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetFinishedExamPage.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetFinishedExamPage.java new file mode 100644 index 00000000..b70a7cf4 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetFinishedExamPage.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 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.session; + +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 GetFinishedExamPage extends RestCall> { + + public GetFinishedExamPage() { + super(new TypeKey<>( + CallType.GET_PAGE, + EntityType.EXAM, + new TypeReference>() { + }), + HttpMethod.GET, + MediaType.APPLICATION_FORM_URLENCODED, + API.EXAM_MONITORING_ENDPOINT + API.EXAM_MONITORING_FINISHED_ENDPOINT); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java index 2fef67f7..d8a52dce 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java @@ -85,7 +85,9 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol @Override public SEBServerAuthorizationContext getAuthorizationContext(final HttpSession session) { - log.debug("Trying to get OAuth2AuthorizationContext from HttpSession: {}", session.getId()); + if (log.isTraceEnabled()) { + log.trace("Trying to get OAuth2AuthorizationContext from HttpSession: {}", session.getId()); + } OAuth2AuthorizationContext context = (OAuth2AuthorizationContext) session.getAttribute(CONTEXT_HOLDER_ATTRIBUTE); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionDetails.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionDetails.java index 885dcc72..86686141 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionDetails.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionDetails.java @@ -198,6 +198,9 @@ public class ClientConnectionDetails { this.connectionData.getIndicatorValues() .forEach(indValue -> { final IndicatorData indData = this.indicatorMapping.get(indValue.getIndicatorId()); + if (indData == null) { + return; + } final double value = indValue.getValue(); final String displayValue = IndicatorValue.getDisplayValue(indValue, indData.indicator.type); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java index 6a338c88..cf19890d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java @@ -88,7 +88,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate private final Table table; private final ColorData colorData; private final Function localizedClientConnectionStatusNameFunction; - private Consumer> selectionListener; + private Consumer selectionListener; private int tableWidth; private boolean needsSort = false; @@ -238,7 +238,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate } } - public ClientConnectionTable withSelectionListener(final Consumer> selectionListener) { + public ClientConnectionTable withSelectionListener(final Consumer selectionListener) { this.selectionListener = selectionListener; return this; } @@ -402,7 +402,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate return; } - this.selectionListener.accept(this.getSelection()); + this.selectionListener.accept(this); } private void notifyTableInfoClick(final Event event) { @@ -496,8 +496,9 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate for (int i = 0; i < this.connectionData.indicatorValues.size(); i++) { final IndicatorValue indicatorValue = this.connectionData.indicatorValues.get(i); - final IndicatorData indicatorData = - ClientConnectionTable.this.indicatorMapping.get(indicatorValue.getIndicatorId()); + final IndicatorData indicatorData = ClientConnectionTable.this.indicatorMapping + .get(indicatorValue.getIndicatorId()); + if (indicatorData == null) { continue; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/InstructionProcessor.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/InstructionProcessor.java index 473fdf15..14f67db6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/InstructionProcessor.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/InstructionProcessor.java @@ -9,6 +9,8 @@ package ch.ethz.seb.sebserver.gui.service.session; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; @@ -55,7 +57,7 @@ public class InstructionProcessor { } public void propagateSEBQuitInstruction( - final Long examId, + final String examId, final String connectionToken, final PageContext pageContext) { @@ -67,40 +69,89 @@ public class InstructionProcessor { } public void propagateSEBQuitInstruction( - final Long examId, + final String examId, final Function, Set> selectionFunction, final PageContext pageContext) { - final Set connectionTokens = selectionFunction - .apply(ClientConnection.getStatusPredicate( - ConnectionStatus.CONNECTION_REQUESTED, - ConnectionStatus.ACTIVE)); + try { + final Set connectionTokens = selectionFunction + .apply(ClientConnection.getStatusPredicate( + ConnectionStatus.CONNECTION_REQUESTED, + ConnectionStatus.ACTIVE)); - if (connectionTokens.isEmpty()) { - log.warn("Empty selection"); - return; + if (connectionTokens.isEmpty()) { + return; + } + + if (log.isDebugEnabled()) { + log.debug("Propagate SEB quit instruction for exam: {} and connections: {}", + examId, + connectionTokens); + } + + final ClientInstruction clientInstruction = new ClientInstruction( + null, + Long.valueOf(examId), + InstructionType.SEB_QUIT, + StringUtils.join(connectionTokens, Constants.LIST_SEPARATOR), + null); + + processInstruction(() -> this.restService.getBuilder(PropagateInstruction.class) + .withURIVariable(API.PARAM_PARENT_MODEL_ID, String.valueOf(examId)) + .withBody(clientInstruction) + .call() + .getOrThrow(), + pageContext); + + } catch (final Exception e) { + pageContext.notifyUnexpectedError(e); } + } - if (log.isDebugEnabled()) { - log.debug("Propagate SEB quit instruction for exam: {} and connections: {}", - examId, - connectionTokens); + public void propagateSEBLockInstruction( + final String examId, + final String message, + final String imageURL, + final String connectionTokens, + final PageContext pageContext) { + + try { + + if (connectionTokens.isEmpty()) { + return; + } + + if (log.isDebugEnabled()) { + log.debug("Propagate SEB lock instruction for exam: {} and connections: {}", + examId, + connectionTokens); + } + + final Map attributes = new HashMap<>(); + if (StringUtils.isNotBlank(message)) { + attributes.put(ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_FORCE_LOCK_SCREEN.MESSAGE, message); + } + if (StringUtils.isNotBlank(imageURL)) { + attributes.put(ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_FORCE_LOCK_SCREEN.IMAGE_URL, imageURL); + } + + final ClientInstruction clientInstruction = new ClientInstruction( + null, + Long.valueOf(examId), + InstructionType.SEB_FORCE_LOCK_SCREEN, + connectionTokens, + attributes); + + processInstruction(() -> this.restService.getBuilder(PropagateInstruction.class) + .withURIVariable(API.PARAM_PARENT_MODEL_ID, examId) + .withBody(clientInstruction) + .call() + .getOrThrow(), + pageContext); + + } catch (final Exception e) { + pageContext.notifyUnexpectedError(e); } - - final ClientInstruction clientInstruction = new ClientInstruction( - null, - examId, - InstructionType.SEB_QUIT, - StringUtils.join(connectionTokens, Constants.LIST_SEPARATOR), - null); - - processInstruction(() -> this.restService.getBuilder(PropagateInstruction.class) - .withURIVariable(API.PARAM_PARENT_MODEL_ID, String.valueOf(examId)) - .withBody(clientInstruction) - .call() - .getOrThrow(), - pageContext); - } public void disableConnection( @@ -113,6 +164,7 @@ public class InstructionProcessor { ConnectionStatus.CONNECTION_REQUESTED, ConnectionStatus.UNDEFINED, ConnectionStatus.CLOSED, + ConnectionStatus.ACTIVE, ConnectionStatus.AUTHENTICATED)); if (connectionTokens.isEmpty()) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/MonitoringProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/MonitoringProctoringService.java index 26f5d832..a6b70723 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/MonitoringProctoringService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/MonitoringProctoringService.java @@ -38,6 +38,7 @@ import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringRoomConnection; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; +import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; @@ -229,7 +230,9 @@ public class MonitoringProctoringService { } }); - updateTownhallButton(proctoringGUIService, pageContext); + if (proctoringSettings.enabledFeatures.contains(ProctoringFeature.TOWN_HALL)) { + updateTownhallButton(proctoringGUIService, pageContext); + } } private void showCollectingRoomPopup( diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java index 0d0fcb3b..152a8706 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java @@ -12,6 +12,7 @@ import static ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService.POLYGLO import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; @@ -24,8 +25,6 @@ import java.util.stream.Collectors; import org.eclipse.rap.rwt.RWT; import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; @@ -46,6 +45,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.GrantEntity; +import ch.ethz.seb.sebserver.gbl.model.ModelIdAware; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.PageSortOrder; import ch.ethz.seb.sebserver.gbl.util.Utils; @@ -60,7 +60,7 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; import io.micrometer.core.instrument.util.StringUtils; -public class EntityTable { +public class EntityTable { private static final Logger log = LoggerFactory.getLogger(EntityTable.class); @@ -92,11 +92,14 @@ public class EntityTable { private final TableFilter filter; private final Table table; private final TableNavigator navigator; + private final MultiValueMap staticQueryParams; private final BiConsumer rowDecorator; - private final Consumer> selectionListener; + private final Consumer> selectionListener; private final Consumer contentChangeListener; + private final Set multiselection; + private final String defaultSortColumn; private final PageSortOrder defaultSortOrder; @@ -122,7 +125,7 @@ public class EntityTable { final boolean hideNavigation, final MultiValueMap staticQueryParams, final BiConsumer rowDecorator, - final Consumer> selectionListener, + final Consumer> selectionListener, final Consumer contentChangeListener, final String defaultSortColumn, final PageSortOrder defaultSortOrder) { @@ -187,6 +190,8 @@ public class EntityTable { this.table.setData(RWT.MARKUP_ENABLED, Boolean.TRUE); } + this.multiselection = ((type & SWT.MULTI) > 0) ? new HashSet<>() : null; + if (defaultActionFunction != null) { final PageAction defaultAction = defaultActionFunction.apply(this); if (defaultAction != null) { @@ -204,23 +209,10 @@ public class EntityTable { }); } } - this.table.addListener(SWT.MouseDown, event -> { - if (event.button == Constants.RWT_MOUSE_BUTTON_1) { - return; - } - final Rectangle bounds = event.getBounds(); - final Point point = new Point(bounds.x, bounds.y); - final TableItem item = this.table.getItem(point); - if (item == null) { - return; - } - - for (int i = 0; i < columns.size(); i++) { - final Rectangle itemBounds = item.getBounds(i); - if (itemBounds.contains(point)) { - handleCellSelection(item, i); - return; - } + this.table.addListener(SWT.Selection, event -> { + if (this.multiselection != null && event.item != null) { + handleMultiSelection((TableItem) event.item); + event.doit = false; } }); @@ -271,6 +263,10 @@ public class EntityTable { return this.table.getItemCount() > 0; } + public boolean hasSelection() { + return (this.multiselection != null && !this.multiselection.isEmpty()) || this.table.getSelectionCount() > 0; + } + public void setPageSize(final int pageSize) { this.pageSize = pageSize; updateTableRows( @@ -304,6 +300,10 @@ public class EntityTable { applyFilter(); } + public void updateCurrentPage() { + this.selectPage(this.pageNumber); + } + public void applyFilter() { try { @@ -368,7 +368,21 @@ public class EntityTable { return null; } - return getRowDataId(selection[0]); + return getEntityKey(selection[0]); + } + + public Set getMultiSelection() { + if (this.multiselection == null) { + return getPageSelectionData() + .stream() + .map(row -> new EntityKey(row.getModelId(), getEntityType())) + .collect(Collectors.toSet()); + } + + return this.multiselection + .stream() + .map(modelId -> new EntityKey(modelId, getEntityType())) + .collect(Collectors.toSet()); } public ROW getFirstRowData() { @@ -393,7 +407,7 @@ public class EntityTable { return getRowData(selection[0]); } - public Set getSelectedROWData() { + public Set getPageSelectionData() { final TableItem[] selection = this.table.getSelection(); if (selection == null || selection.length == 0) { return Collections.emptySet(); @@ -404,10 +418,6 @@ public class EntityTable { .collect(Collectors.toSet()); } - public Set getSelection() { - return getSelection(null); - } - public Set getSelection(final Predicate grantCheck) { final TableItem[] selection = this.table.getSelection(); if (selection == null) { @@ -416,7 +426,7 @@ public class EntityTable { return Arrays.stream(selection) .filter(item -> grantCheck == null || grantCheck.test(getRowData(item))) - .map(this::getRowDataId) + .map(this::getEntityKey) .collect(Collectors.toSet()); } @@ -609,7 +619,7 @@ public class EntityTable { return (ROW) item.getData(TABLE_ROW_DATA); } - private EntityKey getRowDataId(final TableItem item) { + private EntityKey getEntityKey(final TableItem item) { final ROW rowData = getRowData(item); if (rowData instanceof Entity) { return ((Entity) rowData).getEntityKey(); @@ -617,6 +627,11 @@ public class EntityTable { return null; } + private String getModelId(final TableItem item) { + final ROW rowData = getRowData(item); + return (rowData != null) ? rowData.getModelId() : null; + } + private void updateValues(final EntityTable table) { final TableItem[] items = table.table.getItems(); final TableColumn[] columns = table.table.getColumns(); @@ -672,16 +687,12 @@ public class EntityTable { } } - private void handleCellSelection(final TableItem item, final int index) { - // TODO handle selection tool-tips on cell level - } - private void notifySelectionChange() { if (this.selectionListener == null) { return; } - this.selectionListener.accept(this.getSelectedROWData()); + this.selectionListener.accept(this); } private void updateCurrentPageAttr() { @@ -705,7 +716,7 @@ public class EntityTable { return 1; } } catch (final Exception e) { - log.error("Failed to get sort attribute form current user attributes", e); + log.error("Failed to get sort attribute from current user attributes", e); return 1; } } @@ -746,7 +757,7 @@ public class EntityTable { setTableSort(); } catch (final Exception e) { - log.error("Failed to get sort attribute form current user attributes", e); + log.error("Failed to get sort attribute from current user attributes", e); } } @@ -783,17 +794,52 @@ public class EntityTable { .getCurrentUser() .getAttribute(this.filterAttrName)); } catch (final Exception e) { - log.error("Failed to get filter attributes form current user attributes", e); + log.error("Failed to get filter attributes from current user attributes", e); } } } private void notifyContentChange() { + multiselectFromPage(); + if (this.contentChangeListener != null) { this.contentChangeListener.accept(this.table.getItemCount()); } } + private void handleMultiSelection(final TableItem item) { + if (this.multiselection != null) { + final String modelId = getModelId(item); + if (this.multiselection.contains(modelId)) { + this.multiselection.remove(modelId); + } else { + this.multiselection.add(modelId); + Arrays.asList(this.table.getSelection()) + .stream() + .forEach(i -> this.multiselection.add(getModelId(i))); + } + multiselectFromPage(); + } + } + + private void multiselectFromPage() { + if (this.multiselection != null) { + Arrays.asList(this.table.getItems()) + .stream() + .forEach(item -> { + final int index = this.table.indexOf(item); + if (this.multiselection.contains(getModelId(item))) { + if (!this.table.isSelected(index)) { + this.table.select(index); + } + } else { + this.table.deselect(index); + } + }); + notifySelectionChange(); + } + } + public void refreshPageSize() { if (this.pageSupplier.newBuilder() .withPaging(this.pageNumber, this.pageSize) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableBuilder.java index 6275ad97..3542835b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableBuilder.java @@ -10,7 +10,6 @@ package ch.ethz.seb.sebserver.gui.table; import java.util.ArrayList; import java.util.List; -import java.util.Set; import java.util.function.BiConsumer; import java.util.function.BooleanSupplier; import java.util.function.Consumer; @@ -23,6 +22,7 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.ModelIdAware; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.PageSortOrder; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; @@ -31,7 +31,7 @@ import ch.ethz.seb.sebserver.gui.service.page.PageService; import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; -public class TableBuilder { +public class TableBuilder { private final String name; private final PageService pageService; @@ -47,7 +47,7 @@ public class TableBuilder { private boolean hideNavigation = false; private Function, PageSupplier.Builder> restCallAdapter; private BiConsumer rowDecorator; - private Consumer> selectionListener; + private Consumer> selectionListener; private Consumer contentChangeListener; private boolean markupEnabled = false; private String defaultSortColumn = null; @@ -151,7 +151,7 @@ public class TableBuilder { return this; } - public TableBuilder withSelectionListener(final Consumer> selectionListener) { + public TableBuilder withSelectionListener(final Consumer> selectionListener) { this.selectionListener = selectionListener; return this; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java index 78221463..6a9646c5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java @@ -32,6 +32,7 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.model.ModelIdAware; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Utils; @@ -41,7 +42,7 @@ import ch.ethz.seb.sebserver.gui.widget.Selection; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; -public class TableFilter { +public class TableFilter { private static final Logger log = LoggerFactory.getLogger(TableFilter.class); @@ -337,6 +338,9 @@ public class TableFilter { TableFilter.this.entityTable.applyFilter(); } }); + this.textInput.addListener(SWT.FocusOut, event -> { + TableFilter.this.entityTable.applyFilter(); + }); return this; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java index 36d91d24..2033c806 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java @@ -33,6 +33,7 @@ public final class MultiSelectionCheckbox extends Composite implements Selection private Listener listener = null; private final Map checkboxes; + private final String testKey; MultiSelectionCheckbox(final Composite parent, final String testKey) { super(parent, SWT.NONE); @@ -42,6 +43,7 @@ public final class MultiSelectionCheckbox extends Composite implements Selection gridLayout.marginHeight = 0; gridLayout.marginWidth = 0; setLayout(gridLayout); + this.testKey = testKey; if (testKey != null) { WidgetFactory.setTestId(this, testKey); } @@ -69,7 +71,7 @@ public final class MultiSelectionCheckbox extends Composite implements Selection final Button button = new Button(this, SWT.CHECK); button.setText(tuple._2); WidgetFactory.setARIALabel(button, tuple._2); - WidgetFactory.setTestId(button, tuple._1); + WidgetFactory.setTestId(button, (this.testKey != null) ? this.testKey + "_" + tuple._1 : tuple._1); final GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, true); button.setLayoutData(gridData); button.setData(OPTION_VALUE, tuple._1); @@ -78,8 +80,6 @@ public final class MultiSelectionCheckbox extends Composite implements Selection this.listener.handleEvent(event); } }); - WidgetFactory.setTestId(button, tuple._1); - WidgetFactory.setARIALabel(button, tuple._2); this.checkboxes.put(tuple._1, button); try { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCombo.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCombo.java index 94988016..8f7dd457 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCombo.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCombo.java @@ -48,6 +48,7 @@ public final class MultiSelectionCombo extends Composite implements Selection { private final Text textInput; private final GridData textCell; private final Composite updateAnchor; + private final String testKey; private Listener listener = null; @@ -59,6 +60,7 @@ public final class MultiSelectionCombo extends Composite implements Selection { super(parent, SWT.NONE); this.widgetFactory = widgetFactory; + this.testKey = locTextPrefix; final GridLayout gridLayout = new GridLayout(); gridLayout.verticalSpacing = 1; @@ -175,6 +177,9 @@ public final class MultiSelectionCombo extends Composite implements Selection { label.addListener(SWT.MouseDoubleClick, this::removeComboSelection); this.selectionControls.add(label); + WidgetFactory.setARIALabel(label, item._2); + WidgetFactory.setTestId(label, (this.testKey != null) ? this.testKey + "_" + item._1 : item._1); + this.availableValues.remove(item); PageService.updateScrolledComposite(this); this.updateAnchor.layout(true, true); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/PasswordInput.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/PasswordInput.java index c8868f74..fc0965be 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/PasswordInput.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/PasswordInput.java @@ -42,12 +42,13 @@ public class PasswordInput extends Composite { public PasswordInput( final Composite parent, final WidgetFactory widgetFactory, - final LocTextKey label) { + final LocTextKey ariaLabel, + final LocTextKey testLabel) { super(parent, SWT.NONE); - this.label = widgetFactory.getI18nSupport().getText(label); - this.testKey = (label != null) ? label.name : null; + this.label = widgetFactory.getI18nSupport().getText(ariaLabel); + this.testKey = testLabel != null ? testLabel.name : null; GridLayout gridLayout = new GridLayout(2, false); gridLayout.horizontalSpacing = 0; gridLayout.verticalSpacing = 0; @@ -96,7 +97,9 @@ public class PasswordInput extends Composite { SWT.LEFT | SWT.BORDER | (buildPassword ? SWT.PASSWORD : SWT.NONE)); final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); passwordInput.setLayoutData(gridData); - passwordInput.setText(value != null ? value : StringUtils.EMPTY); + passwordInput.setText(value != null + ? Utils.escapeHTML_XML_EcmaScript(value) + : StringUtils.EMPTY); if (!buildPassword) { passwordInput.setEditable(false); } else { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java index 4ccf29bd..10518c8c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java @@ -75,7 +75,7 @@ public final class RadioSelection extends Composite implements Selection { this.listener.handleEvent(event); } }); - WidgetFactory.setTestId(button, (this.testKey != null) ? this.testKey + tuple._1 : tuple._1); + WidgetFactory.setTestId(button, (this.testKey != null) ? this.testKey + "_" + tuple._1 : tuple._1); WidgetFactory.setARIALabel(button, tuple._2); this.radioButtons.put(tuple._1, button); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ThresholdList.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ThresholdList.java index 57572f61..14bf03b5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ThresholdList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ThresholdList.java @@ -121,17 +121,18 @@ public final class ThresholdList extends Composite { public Collection getThresholds() { removeInvalidListEntries(); - return this.thresholds + final List collect = this.thresholds .stream() .map(entry -> new Threshold(entry.getValue(), entry.getColor(), null /* TODO add icon selection here */)) .collect(Collectors.toList()); + return collect; } private void removeInvalidListEntries() { this.thresholds .stream() - .filter(entry -> entry.getValue() == null || StringUtils.isBlank(entry.getColor())) + .filter(entry -> entry.getValue() == null && StringUtils.isBlank(entry.getColor())) .collect(Collectors.toList()) .forEach(this::removeThreshold); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java index 08f18eaa..7ee7f9f6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java @@ -122,6 +122,7 @@ public class WidgetFactory { SECURE("secure.png"), NEW("new.png"), DELETE("delete.png"), + ARCHIVE("archive.png"), SEARCH("lens.png"), UNDO("undo.png"), COLOR("color.png"), @@ -206,6 +207,7 @@ public class WidgetFactory { MESSAGE("message"), ERROR("error"), WARNING("warning"), + DISABLED("disabled"), CONFIG_INPUT_READONLY("inputreadonly"), DARK_COLOR_LABEL("colordark"), diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java index 6745b1d0..3be4b33c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java @@ -22,6 +22,7 @@ import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; @@ -72,12 +73,18 @@ public class WebserviceInfo { private final WebserviceInfoDAO webserviceInfoDAO; private boolean isMaster = false; + @Value("${sebserver.webservice.api.admin.accessTokenValiditySeconds:3600}") + private int adminAccessTokenValSec; + @Value("${sebserver.webservice.api.admin.refreshTokenValiditySeconds:-1}") + private int adminRefreshTokenValSec; + @Value("${sebserver.webservice.api.exam.accessTokenValiditySeconds:43200}") + private int examAPITokenValiditySeconds; + public WebserviceInfo( final WebserviceInfoDAO webserviceInfoDAO, final Environment environment) { this.webserviceInfoDAO = webserviceInfoDAO; - this.webserviceUUID = UUID.randomUUID().toString(); this.sebServerVersion = environment.getRequiredProperty(VERSION_KEY); this.testProperty = environment.getProperty(WEB_SERVICE_TEST_PROPERTY, "NOT_AVAILABLE"); this.httpScheme = environment.getRequiredProperty(WEB_SERVICE_HTTP_SCHEME_KEY); @@ -87,6 +94,9 @@ public class WebserviceInfo { this.webserverPort = environment.getProperty(WEB_SERVICE_HTTP_PORT); this.discoveryEndpoint = environment.getRequiredProperty(WEB_SERVICE_EXAM_API_DISCOVERY_ENDPOINT_KEY); this.contextPath = environment.getProperty(WEB_SERVICE_CONTEXT_PATH, ""); + this.webserviceUUID = UUID.randomUUID().toString() + + Constants.UNDERLINE + + this.sebServerVersion; this.distributedUpdateInterval = environment.getProperty( "sebserver.webservice.distributed.updateInterval", @@ -201,7 +211,8 @@ public class WebserviceInfo { try { return InetAddress.getLocalHost().getHostName(); } catch (final UnknownHostException e) { - return null; + log.error("Failed to get local host name: {}", e.getMessage()); + return Constants.EMPTY_NOTE; } } @@ -209,7 +220,8 @@ public class WebserviceInfo { try { return InetAddress.getLocalHost().getHostAddress(); } catch (final UnknownHostException e) { - return null; + log.error("Failed to get local host address: {}", e.getMessage()); + return Constants.EMPTY_NOTE; } } @@ -221,7 +233,7 @@ public class WebserviceInfo { return InetAddress.getLoopbackAddress().getHostAddress(); } - /** Get the server URL prefix in form of; + /** Get the server URL prefix in the form of; * [scheme{http|https}]://[server-address{DNS-name|IP}]:[port] * * E.g.: https://seb.server.ch:8080 @@ -249,6 +261,18 @@ public class WebserviceInfo { .orElse(null); } + public int getAdminAccessTokenValSec() { + return this.adminAccessTokenValSec; + } + + public int getAdminRefreshTokenValSec() { + return this.adminRefreshTokenValSec; + } + + public int getExamAPITokenValiditySeconds() { + return this.examAPITokenValiditySeconds; + } + @Override public String toString() { final StringBuilder builder = new StringBuilder(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java index b863ca27..0c3f4026 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java @@ -8,9 +8,6 @@ package ch.ethz.seb.sebserver.webservice; -import java.net.InetAddress; -import java.net.UnknownHostException; - import javax.annotation.PreDestroy; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; @@ -94,7 +91,6 @@ public class WebserviceInit implements ApplicationListener "); this.registerWebservice(); - } SEBServerInit.INIT_LOGGER.info("----> "); @@ -126,21 +122,15 @@ public class WebserviceInit implements ApplicationListener "); - SEBServerInit.INIT_LOGGER.info("----> Server address: {}", this.environment.getProperty("server.address")); - SEBServerInit.INIT_LOGGER.info("----> Server port: {}", this.environment.getProperty("server.port")); - SEBServerInit.INIT_LOGGER.info("---->"); - SEBServerInit.INIT_LOGGER.info("----> Local-Host address: {}", InetAddress.getLocalHost().getHostAddress()); - SEBServerInit.INIT_LOGGER.info("----> Local-Host name: {}", InetAddress.getLocalHost().getHostName()); - SEBServerInit.INIT_LOGGER.info("---->"); - SEBServerInit.INIT_LOGGER.info("----> Remote-Host address: {}", - InetAddress.getLoopbackAddress().getHostAddress()); - SEBServerInit.INIT_LOGGER.info("----> Remote-Host name: {}", - InetAddress.getLoopbackAddress().getHostName()); - } catch (final UnknownHostException e) { - SEBServerInit.INIT_LOGGER.error("Unknown Host: ", e); - } + SEBServerInit.INIT_LOGGER.info("----> "); + SEBServerInit.INIT_LOGGER.info("----> Server address: {}", this.environment.getProperty("server.address")); + SEBServerInit.INIT_LOGGER.info("----> Server port: {}", this.environment.getProperty("server.port")); + SEBServerInit.INIT_LOGGER.info("---->"); + SEBServerInit.INIT_LOGGER.info("----> Local-Host address: {}", this.webserviceInfo.getLocalHostAddress()); + SEBServerInit.INIT_LOGGER.info("----> Local-Host name: {}", this.webserviceInfo.getLocalHostName()); + SEBServerInit.INIT_LOGGER.info("---->"); + SEBServerInit.INIT_LOGGER.info("----> Remote-Host address: {}", this.webserviceInfo.getLoopbackHostAddress()); + SEBServerInit.INIT_LOGGER.info("----> Remote-Host name: {}", this.webserviceInfo.getLoopbackHostName()); SEBServerInit.INIT_LOGGER.info("---->"); SEBServerInit.INIT_LOGGER.info("----> Context Path: {}", this.webserviceInfo.getContextPath()); @@ -150,6 +140,14 @@ public class WebserviceInit implements ApplicationListener"); SEBServerInit.INIT_LOGGER.info("----> HTTP Scheme {}", this.webserviceInfo.getHttpScheme()); SEBServerInit.INIT_LOGGER.info("---->"); + SEBServerInit.INIT_LOGGER.info("----> Access-Tokens:"); + SEBServerInit.INIT_LOGGER.info( + "----> admin API access token validity: " + this.webserviceInfo.getAdminAccessTokenValSec() + "s"); + SEBServerInit.INIT_LOGGER.info( + "----> admin API refresh token validity: " + this.webserviceInfo.getAdminRefreshTokenValSec() + "s"); + SEBServerInit.INIT_LOGGER.info( + "----> exam API access token validity: " + this.webserviceInfo.getExamAPITokenValiditySeconds() + "s"); + SEBServerInit.INIT_LOGGER.info("----> "); SEBServerInit.INIT_LOGGER.info("----> Property Override Test: {}", this.webserviceInfo.getTestProperty()); SEBServerInit.INIT_LOGGER.info("---->"); @@ -162,7 +160,7 @@ public class WebserviceInit implements ApplicationListener Successfully register Webservice instance. uuid: {}, address: {}", diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/ClientConnectionMinMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/ClientConnectionTokenMapper.java similarity index 73% rename from src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/ClientConnectionMinMapper.java rename to src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/ClientConnectionTokenMapper.java index b8b9ee20..35863138 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/ClientConnectionMinMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/ClientConnectionTokenMapper.java @@ -25,20 +25,19 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordDynamicSqlSupport; @Mapper -public interface ClientConnectionMinMapper { +public interface ClientConnectionTokenMapper { @SelectProvider(type = SqlProviderAdapter.class, method = "select") Long num(SelectStatementProvider selectStatement); @SelectProvider(type = SqlProviderAdapter.class, method = "select") - @ResultType(ClientConnectionMinRecord.class) + @ResultType(ClientConnectionTokenRecord.class) @ConstructorArgs({ - @Arg(column = "connection_token", javaType = String.class, jdbcType = JdbcType.VARCHAR, id = true), - @Arg(column = "update_time", javaType = Long.class, jdbcType = JdbcType.BIGINT), + @Arg(column = "connection_token", javaType = String.class, jdbcType = JdbcType.VARCHAR, id = true) }) - Collection selectMany(SelectStatementProvider select); + Collection selectMany(SelectStatementProvider select); - default QueryExpressionDSL>> selectByExample() { + default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper( this::selectMany, @@ -48,17 +47,12 @@ public interface ClientConnectionMinMapper { .from(ClientConnectionRecordDynamicSqlSupport.clientConnectionRecord); } - final class ClientConnectionMinRecord { + final class ClientConnectionTokenRecord { public final String connection_token; - public final Long update_time; - - public ClientConnectionMinRecord( - final String connection_token, - final Long update_time) { + public ClientConnectionTokenRecord(final String connection_token) { this.connection_token = connection_token; - this.update_time = update_time; } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/AdditionalAttributeRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/AdditionalAttributeRecordDynamicSqlSupport.java index 4b694c1e..a4085f45 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/AdditionalAttributeRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/AdditionalAttributeRecordDynamicSqlSupport.java @@ -6,25 +6,25 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class AdditionalAttributeRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.439+02:00", comments="Source Table: additional_attributes") public static final AdditionalAttributeRecord additionalAttributeRecord = new AdditionalAttributeRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.440+02:00", comments="Source field: additional_attributes.id") public static final SqlColumn id = additionalAttributeRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.entity_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.440+02:00", comments="Source field: additional_attributes.entity_type") public static final SqlColumn entityType = additionalAttributeRecord.entityType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.entity_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.440+02:00", comments="Source field: additional_attributes.entity_id") public static final SqlColumn entityId = additionalAttributeRecord.entityId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.440+02:00", comments="Source field: additional_attributes.name") public static final SqlColumn name = additionalAttributeRecord.name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.440+02:00", comments="Source field: additional_attributes.value") public static final SqlColumn value = additionalAttributeRecord.value; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.439+02:00", comments="Source Table: additional_attributes") public static final class AdditionalAttributeRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/AdditionalAttributeRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/AdditionalAttributeRecordMapper.java index c601e88e..6c61db61 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/AdditionalAttributeRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/AdditionalAttributeRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface AdditionalAttributeRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.440+02:00", comments="Source Table: additional_attributes") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.440+02:00", comments="Source Table: additional_attributes") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -56,7 +56,7 @@ public interface AdditionalAttributeRecordMapper { }) AdditionalAttributeRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -67,22 +67,22 @@ public interface AdditionalAttributeRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.206+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.206+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(additionalAttributeRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.206+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, additionalAttributeRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.206+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, additionalAttributeRecord) .where(id, isEqualTo(id_)) @@ -90,7 +90,7 @@ public interface AdditionalAttributeRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.206+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") default int insert(AdditionalAttributeRecord record) { return insert(SqlBuilder.insert(record) .into(additionalAttributeRecord) @@ -102,7 +102,7 @@ public interface AdditionalAttributeRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.206+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") default int insertSelective(AdditionalAttributeRecord record) { return insert(SqlBuilder.insert(record) .into(additionalAttributeRecord) @@ -114,19 +114,19 @@ public interface AdditionalAttributeRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.206+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, entityType, entityId, name, value) .from(additionalAttributeRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.206+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, entityType, entityId, name, value) .from(additionalAttributeRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.206+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") default AdditionalAttributeRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, entityType, entityId, name, value) .from(additionalAttributeRecord) @@ -135,7 +135,7 @@ public interface AdditionalAttributeRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.206+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") default UpdateDSL> updateByExample(AdditionalAttributeRecord record) { return UpdateDSL.updateWithMapper(this::update, additionalAttributeRecord) .set(entityType).equalTo(record::getEntityType) @@ -144,7 +144,7 @@ public interface AdditionalAttributeRecordMapper { .set(value).equalTo(record::getValue); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.206+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") default UpdateDSL> updateByExampleSelective(AdditionalAttributeRecord record) { return UpdateDSL.updateWithMapper(this::update, additionalAttributeRecord) .set(entityType).equalToWhenPresent(record::getEntityType) @@ -153,7 +153,7 @@ public interface AdditionalAttributeRecordMapper { .set(value).equalToWhenPresent(record::getValue); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.206+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.441+02:00", comments="Source Table: additional_attributes") default int updateByPrimaryKey(AdditionalAttributeRecord record) { return UpdateDSL.updateWithMapper(this::update, additionalAttributeRecord) .set(entityType).equalTo(record::getEntityType) @@ -165,7 +165,7 @@ public interface AdditionalAttributeRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.206+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.442+02:00", comments="Source Table: additional_attributes") default int updateByPrimaryKeySelective(AdditionalAttributeRecord record) { return UpdateDSL.updateWithMapper(this::update, additionalAttributeRecord) .set(entityType).equalToWhenPresent(record::getEntityType) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/BatchActionRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/BatchActionRecordDynamicSqlSupport.java index 3138a1c5..6ca5256a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/BatchActionRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/BatchActionRecordDynamicSqlSupport.java @@ -6,38 +6,48 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class BatchActionRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.218+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source Table: batch_action") public static final BatchActionRecord batchActionRecord = new BatchActionRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.218+01:00", comments="Source field: batch_action.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.454+02:00", comments="Source field: batch_action.id") public static final SqlColumn id = batchActionRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.218+01:00", comments="Source field: batch_action.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.454+02:00", comments="Source field: batch_action.institution_id") public static final SqlColumn institutionId = batchActionRecord.institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.218+01:00", comments="Source field: batch_action.action_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.454+02:00", comments="Source field: batch_action.owner") + public static final SqlColumn owner = batchActionRecord.owner; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.454+02:00", comments="Source field: batch_action.action_type") public static final SqlColumn actionType = batchActionRecord.actionType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.218+01:00", comments="Source field: batch_action.source_ids") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.454+02:00", comments="Source field: batch_action.attributes") + public static final SqlColumn attributes = batchActionRecord.attributes; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.454+02:00", comments="Source field: batch_action.source_ids") public static final SqlColumn sourceIds = batchActionRecord.sourceIds; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.219+01:00", comments="Source field: batch_action.successful") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.454+02:00", comments="Source field: batch_action.successful") public static final SqlColumn successful = batchActionRecord.successful; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.219+01:00", comments="Source field: batch_action.last_update") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.454+02:00", comments="Source field: batch_action.last_update") public static final SqlColumn lastUpdate = batchActionRecord.lastUpdate; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.219+01:00", comments="Source field: batch_action.processor_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.454+02:00", comments="Source field: batch_action.processor_id") public static final SqlColumn processorId = batchActionRecord.processorId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.218+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source Table: batch_action") public static final class BatchActionRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); public final SqlColumn institutionId = column("institution_id", JDBCType.BIGINT); + public final SqlColumn owner = column("owner", JDBCType.VARCHAR); + public final SqlColumn actionType = column("action_type", JDBCType.VARCHAR); + public final SqlColumn attributes = column("attributes", JDBCType.VARCHAR); + public final SqlColumn sourceIds = column("source_ids", JDBCType.VARCHAR); public final SqlColumn successful = column("successful", JDBCType.VARCHAR); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/BatchActionRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/BatchActionRecordMapper.java index fae0204a..813ab5aa 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/BatchActionRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/BatchActionRecordMapper.java @@ -32,25 +32,27 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface BatchActionRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.219+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.454+02:00", comments="Source Table: batch_action") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.219+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.454+02:00", comments="Source Table: batch_action") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.219+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.454+02:00", comments="Source Table: batch_action") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.219+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.454+02:00", comments="Source Table: batch_action") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @Arg(column="institution_id", javaType=Long.class, jdbcType=JdbcType.BIGINT), + @Arg(column="owner", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="action_type", javaType=String.class, jdbcType=JdbcType.VARCHAR), + @Arg(column="attributes", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="source_ids", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="successful", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="last_update", javaType=Long.class, jdbcType=JdbcType.BIGINT), @@ -58,12 +60,14 @@ public interface BatchActionRecordMapper { }) BatchActionRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.219+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.454+02:00", comments="Source Table: batch_action") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @Arg(column="institution_id", javaType=Long.class, jdbcType=JdbcType.BIGINT), + @Arg(column="owner", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="action_type", javaType=String.class, jdbcType=JdbcType.VARCHAR), + @Arg(column="attributes", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="source_ids", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="successful", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="last_update", javaType=Long.class, jdbcType=JdbcType.BIGINT), @@ -71,22 +75,22 @@ public interface BatchActionRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.455+02:00", comments="Source Table: batch_action") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.455+02:00", comments="Source Table: batch_action") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(batchActionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.455+02:00", comments="Source Table: batch_action") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, batchActionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.455+02:00", comments="Source Table: batch_action") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, batchActionRecord) .where(id, isEqualTo(id_)) @@ -94,12 +98,14 @@ public interface BatchActionRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.455+02:00", comments="Source Table: batch_action") default int insert(BatchActionRecord record) { return insert(SqlBuilder.insert(record) .into(batchActionRecord) .map(institutionId).toProperty("institutionId") + .map(owner).toProperty("owner") .map(actionType).toProperty("actionType") + .map(attributes).toProperty("attributes") .map(sourceIds).toProperty("sourceIds") .map(successful).toProperty("successful") .map(lastUpdate).toProperty("lastUpdate") @@ -108,12 +114,14 @@ public interface BatchActionRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.455+02:00", comments="Source Table: batch_action") default int insertSelective(BatchActionRecord record) { return insert(SqlBuilder.insert(record) .into(batchActionRecord) .map(institutionId).toPropertyWhenPresent("institutionId", record::getInstitutionId) + .map(owner).toPropertyWhenPresent("owner", record::getOwner) .map(actionType).toPropertyWhenPresent("actionType", record::getActionType) + .map(attributes).toPropertyWhenPresent("attributes", record::getAttributes) .map(sourceIds).toPropertyWhenPresent("sourceIds", record::getSourceIds) .map(successful).toPropertyWhenPresent("successful", record::getSuccessful) .map(lastUpdate).toPropertyWhenPresent("lastUpdate", record::getLastUpdate) @@ -122,54 +130,60 @@ public interface BatchActionRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.455+02:00", comments="Source Table: batch_action") default QueryExpressionDSL>> selectByExample() { - return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, actionType, sourceIds, successful, lastUpdate, processorId) + return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, owner, actionType, attributes, sourceIds, successful, lastUpdate, processorId) .from(batchActionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.455+02:00", comments="Source Table: batch_action") default QueryExpressionDSL>> selectDistinctByExample() { - return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, actionType, sourceIds, successful, lastUpdate, processorId) + return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, owner, actionType, attributes, sourceIds, successful, lastUpdate, processorId) .from(batchActionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.455+02:00", comments="Source Table: batch_action") default BatchActionRecord selectByPrimaryKey(Long id_) { - return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, actionType, sourceIds, successful, lastUpdate, processorId) + return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, owner, actionType, attributes, sourceIds, successful, lastUpdate, processorId) .from(batchActionRecord) .where(id, isEqualTo(id_)) .build() .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.455+02:00", comments="Source Table: batch_action") default UpdateDSL> updateByExample(BatchActionRecord record) { return UpdateDSL.updateWithMapper(this::update, batchActionRecord) .set(institutionId).equalTo(record::getInstitutionId) + .set(owner).equalTo(record::getOwner) .set(actionType).equalTo(record::getActionType) + .set(attributes).equalTo(record::getAttributes) .set(sourceIds).equalTo(record::getSourceIds) .set(successful).equalTo(record::getSuccessful) .set(lastUpdate).equalTo(record::getLastUpdate) .set(processorId).equalTo(record::getProcessorId); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.455+02:00", comments="Source Table: batch_action") default UpdateDSL> updateByExampleSelective(BatchActionRecord record) { return UpdateDSL.updateWithMapper(this::update, batchActionRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) + .set(owner).equalToWhenPresent(record::getOwner) .set(actionType).equalToWhenPresent(record::getActionType) + .set(attributes).equalToWhenPresent(record::getAttributes) .set(sourceIds).equalToWhenPresent(record::getSourceIds) .set(successful).equalToWhenPresent(record::getSuccessful) .set(lastUpdate).equalToWhenPresent(record::getLastUpdate) .set(processorId).equalToWhenPresent(record::getProcessorId); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.455+02:00", comments="Source Table: batch_action") default int updateByPrimaryKey(BatchActionRecord record) { return UpdateDSL.updateWithMapper(this::update, batchActionRecord) .set(institutionId).equalTo(record::getInstitutionId) + .set(owner).equalTo(record::getOwner) .set(actionType).equalTo(record::getActionType) + .set(attributes).equalTo(record::getAttributes) .set(sourceIds).equalTo(record::getSourceIds) .set(successful).equalTo(record::getSuccessful) .set(lastUpdate).equalTo(record::getLastUpdate) @@ -179,11 +193,13 @@ public interface BatchActionRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: batch_action") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.455+02:00", comments="Source Table: batch_action") default int updateByPrimaryKeySelective(BatchActionRecord record) { return UpdateDSL.updateWithMapper(this::update, batchActionRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) + .set(owner).equalToWhenPresent(record::getOwner) .set(actionType).equalToWhenPresent(record::getActionType) + .set(attributes).equalToWhenPresent(record::getAttributes) .set(sourceIds).equalToWhenPresent(record::getSourceIds) .set(successful).equalToWhenPresent(record::getSuccessful) .set(lastUpdate).equalToWhenPresent(record::getLastUpdate) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/CertificateRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/CertificateRecordDynamicSqlSupport.java index db469942..0dcd51eb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/CertificateRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/CertificateRecordDynamicSqlSupport.java @@ -6,22 +6,22 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class CertificateRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.212+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.446+02:00", comments="Source Table: certificate") public static final CertificateRecord certificateRecord = new CertificateRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.212+01:00", comments="Source field: certificate.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.447+02:00", comments="Source field: certificate.id") public static final SqlColumn id = certificateRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source field: certificate.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.447+02:00", comments="Source field: certificate.institution_id") public static final SqlColumn institutionId = certificateRecord.institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source field: certificate.aliases") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.447+02:00", comments="Source field: certificate.aliases") public static final SqlColumn aliases = certificateRecord.aliases; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source field: certificate.cert_store") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.447+02:00", comments="Source field: certificate.cert_store") public static final SqlColumn certStore = certificateRecord.certStore; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.212+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.446+02:00", comments="Source Table: certificate") public static final class CertificateRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/CertificateRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/CertificateRecordMapper.java index f8766d94..acf4daa3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/CertificateRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/CertificateRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface CertificateRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.447+02:00", comments="Source Table: certificate") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.447+02:00", comments="Source Table: certificate") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.447+02:00", comments="Source Table: certificate") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.447+02:00", comments="Source Table: certificate") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -55,7 +55,7 @@ public interface CertificateRecordMapper { }) CertificateRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.447+02:00", comments="Source Table: certificate") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -65,22 +65,22 @@ public interface CertificateRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.447+02:00", comments="Source Table: certificate") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.447+02:00", comments="Source Table: certificate") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(certificateRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.447+02:00", comments="Source Table: certificate") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, certificateRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.447+02:00", comments="Source Table: certificate") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, certificateRecord) .where(id, isEqualTo(id_)) @@ -88,7 +88,7 @@ public interface CertificateRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.447+02:00", comments="Source Table: certificate") default int insert(CertificateRecord record) { return insert(SqlBuilder.insert(record) .into(certificateRecord) @@ -99,7 +99,7 @@ public interface CertificateRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.448+02:00", comments="Source Table: certificate") default int insertSelective(CertificateRecord record) { return insert(SqlBuilder.insert(record) .into(certificateRecord) @@ -110,19 +110,19 @@ public interface CertificateRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.448+02:00", comments="Source Table: certificate") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, aliases, certStore) .from(certificateRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.448+02:00", comments="Source Table: certificate") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, aliases, certStore) .from(certificateRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.448+02:00", comments="Source Table: certificate") default CertificateRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, aliases, certStore) .from(certificateRecord) @@ -131,7 +131,7 @@ public interface CertificateRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.448+02:00", comments="Source Table: certificate") default UpdateDSL> updateByExample(CertificateRecord record) { return UpdateDSL.updateWithMapper(this::update, certificateRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -139,7 +139,7 @@ public interface CertificateRecordMapper { .set(certStore).equalTo(record::getCertStore); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.448+02:00", comments="Source Table: certificate") default UpdateDSL> updateByExampleSelective(CertificateRecord record) { return UpdateDSL.updateWithMapper(this::update, certificateRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) @@ -147,7 +147,7 @@ public interface CertificateRecordMapper { .set(certStore).equalToWhenPresent(record::getCertStore); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.448+02:00", comments="Source Table: certificate") default int updateByPrimaryKey(CertificateRecord record) { return UpdateDSL.updateWithMapper(this::update, certificateRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -158,7 +158,7 @@ public interface CertificateRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.213+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.448+02:00", comments="Source Table: certificate") default int updateByPrimaryKeySelective(CertificateRecord record) { return UpdateDSL.updateWithMapper(this::update, certificateRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientConnectionRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientConnectionRecordDynamicSqlSupport.java index 7912f92c..821d561c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientConnectionRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientConnectionRecordDynamicSqlSupport.java @@ -6,61 +6,61 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class ClientConnectionRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.372+02:00", comments="Source Table: client_connection") public static final ClientConnectionRecord clientConnectionRecord = new ClientConnectionRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.372+02:00", comments="Source field: client_connection.id") public static final SqlColumn id = clientConnectionRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.372+02:00", comments="Source field: client_connection.institution_id") public static final SqlColumn institutionId = clientConnectionRecord.institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.141+01:00", comments="Source field: client_connection.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.373+02:00", comments="Source field: client_connection.exam_id") public static final SqlColumn examId = clientConnectionRecord.examId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.141+01:00", comments="Source field: client_connection.status") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.373+02:00", comments="Source field: client_connection.status") public static final SqlColumn status = clientConnectionRecord.status; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.141+01:00", comments="Source field: client_connection.connection_token") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.373+02:00", comments="Source field: client_connection.connection_token") public static final SqlColumn connectionToken = clientConnectionRecord.connectionToken; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.141+01:00", comments="Source field: client_connection.exam_user_session_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.373+02:00", comments="Source field: client_connection.exam_user_session_id") public static final SqlColumn examUserSessionId = clientConnectionRecord.examUserSessionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.141+01:00", comments="Source field: client_connection.client_address") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.373+02:00", comments="Source field: client_connection.client_address") public static final SqlColumn clientAddress = clientConnectionRecord.clientAddress; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.141+01:00", comments="Source field: client_connection.virtual_client_address") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.373+02:00", comments="Source field: client_connection.virtual_client_address") public static final SqlColumn virtualClientAddress = clientConnectionRecord.virtualClientAddress; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.141+01:00", comments="Source field: client_connection.vdi") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.373+02:00", comments="Source field: client_connection.vdi") public static final SqlColumn vdi = clientConnectionRecord.vdi; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.141+01:00", comments="Source field: client_connection.vdi_pair_token") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.374+02:00", comments="Source field: client_connection.vdi_pair_token") public static final SqlColumn vdiPairToken = clientConnectionRecord.vdiPairToken; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.141+01:00", comments="Source field: client_connection.creation_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.374+02:00", comments="Source field: client_connection.creation_time") public static final SqlColumn creationTime = clientConnectionRecord.creationTime; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.141+01:00", comments="Source field: client_connection.update_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.374+02:00", comments="Source field: client_connection.update_time") public static final SqlColumn updateTime = clientConnectionRecord.updateTime; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.141+01:00", comments="Source field: client_connection.remote_proctoring_room_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.374+02:00", comments="Source field: client_connection.remote_proctoring_room_id") public static final SqlColumn remoteProctoringRoomId = clientConnectionRecord.remoteProctoringRoomId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.142+01:00", comments="Source field: client_connection.remote_proctoring_room_update") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.374+02:00", comments="Source field: client_connection.remote_proctoring_room_update") public static final SqlColumn remoteProctoringRoomUpdate = clientConnectionRecord.remoteProctoringRoomUpdate; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.142+01:00", comments="Source field: client_connection.client_machine_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.374+02:00", comments="Source field: client_connection.client_machine_name") public static final SqlColumn clientMachineName = clientConnectionRecord.clientMachineName; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.142+01:00", comments="Source field: client_connection.client_os_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.374+02:00", comments="Source field: client_connection.client_os_name") public static final SqlColumn clientOsName = clientConnectionRecord.clientOsName; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.142+01:00", comments="Source field: client_connection.client_version") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.374+02:00", comments="Source field: client_connection.client_version") public static final SqlColumn clientVersion = clientConnectionRecord.clientVersion; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.372+02:00", comments="Source Table: client_connection") public static final class ClientConnectionRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientConnectionRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientConnectionRecordMapper.java index d40152a9..e6c1f0b5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientConnectionRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientConnectionRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface ClientConnectionRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.142+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.374+02:00", comments="Source Table: client_connection") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.142+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.375+02:00", comments="Source Table: client_connection") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.142+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.375+02:00", comments="Source Table: client_connection") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.142+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.375+02:00", comments="Source Table: client_connection") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -68,7 +68,7 @@ public interface ClientConnectionRecordMapper { }) ClientConnectionRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.142+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.375+02:00", comments="Source Table: client_connection") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -91,22 +91,22 @@ public interface ClientConnectionRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.142+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.375+02:00", comments="Source Table: client_connection") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.142+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.375+02:00", comments="Source Table: client_connection") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(clientConnectionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.142+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.375+02:00", comments="Source Table: client_connection") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, clientConnectionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.142+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.375+02:00", comments="Source Table: client_connection") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, clientConnectionRecord) .where(id, isEqualTo(id_)) @@ -114,7 +114,7 @@ public interface ClientConnectionRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.142+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.375+02:00", comments="Source Table: client_connection") default int insert(ClientConnectionRecord record) { return insert(SqlBuilder.insert(record) .into(clientConnectionRecord) @@ -138,7 +138,7 @@ public interface ClientConnectionRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.143+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.375+02:00", comments="Source Table: client_connection") default int insertSelective(ClientConnectionRecord record) { return insert(SqlBuilder.insert(record) .into(clientConnectionRecord) @@ -162,19 +162,19 @@ public interface ClientConnectionRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.143+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.375+02:00", comments="Source Table: client_connection") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, examId, status, connectionToken, examUserSessionId, clientAddress, virtualClientAddress, vdi, vdiPairToken, creationTime, updateTime, remoteProctoringRoomId, remoteProctoringRoomUpdate, clientMachineName, clientOsName, clientVersion) .from(clientConnectionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.143+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.375+02:00", comments="Source Table: client_connection") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, examId, status, connectionToken, examUserSessionId, clientAddress, virtualClientAddress, vdi, vdiPairToken, creationTime, updateTime, remoteProctoringRoomId, remoteProctoringRoomUpdate, clientMachineName, clientOsName, clientVersion) .from(clientConnectionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.143+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.376+02:00", comments="Source Table: client_connection") default ClientConnectionRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, examId, status, connectionToken, examUserSessionId, clientAddress, virtualClientAddress, vdi, vdiPairToken, creationTime, updateTime, remoteProctoringRoomId, remoteProctoringRoomUpdate, clientMachineName, clientOsName, clientVersion) .from(clientConnectionRecord) @@ -183,7 +183,7 @@ public interface ClientConnectionRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.143+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.376+02:00", comments="Source Table: client_connection") default UpdateDSL> updateByExample(ClientConnectionRecord record) { return UpdateDSL.updateWithMapper(this::update, clientConnectionRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -204,7 +204,7 @@ public interface ClientConnectionRecordMapper { .set(clientVersion).equalTo(record::getClientVersion); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.143+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.376+02:00", comments="Source Table: client_connection") default UpdateDSL> updateByExampleSelective(ClientConnectionRecord record) { return UpdateDSL.updateWithMapper(this::update, clientConnectionRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) @@ -225,7 +225,7 @@ public interface ClientConnectionRecordMapper { .set(clientVersion).equalToWhenPresent(record::getClientVersion); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.143+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.376+02:00", comments="Source Table: client_connection") default int updateByPrimaryKey(ClientConnectionRecord record) { return UpdateDSL.updateWithMapper(this::update, clientConnectionRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -249,7 +249,7 @@ public interface ClientConnectionRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.143+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.377+02:00", comments="Source Table: client_connection") default int updateByPrimaryKeySelective(ClientConnectionRecord record) { return UpdateDSL.updateWithMapper(this::update, clientConnectionRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientEventRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientEventRecordDynamicSqlSupport.java index fbad03f2..206eeb28 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientEventRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientEventRecordDynamicSqlSupport.java @@ -7,31 +7,31 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class ClientEventRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.152+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.387+02:00", comments="Source Table: client_event") public static final ClientEventRecord clientEventRecord = new ClientEventRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.152+01:00", comments="Source field: client_event.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.388+02:00", comments="Source field: client_event.id") public static final SqlColumn id = clientEventRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.152+01:00", comments="Source field: client_event.client_connection_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.388+02:00", comments="Source field: client_event.client_connection_id") public static final SqlColumn clientConnectionId = clientEventRecord.clientConnectionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source field: client_event.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.388+02:00", comments="Source field: client_event.type") public static final SqlColumn type = clientEventRecord.type; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source field: client_event.client_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.388+02:00", comments="Source field: client_event.client_time") public static final SqlColumn clientTime = clientEventRecord.clientTime; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source field: client_event.server_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.388+02:00", comments="Source field: client_event.server_time") public static final SqlColumn serverTime = clientEventRecord.serverTime; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source field: client_event.numeric_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.388+02:00", comments="Source field: client_event.numeric_value") public static final SqlColumn numericValue = clientEventRecord.numericValue; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source field: client_event.text") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.388+02:00", comments="Source field: client_event.text") public static final SqlColumn text = clientEventRecord.text; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.152+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.387+02:00", comments="Source Table: client_event") public static final class ClientEventRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientEventRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientEventRecordMapper.java index 5ab1c2b7..829728f1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientEventRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientEventRecordMapper.java @@ -32,19 +32,19 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface ClientEventRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.388+02:00", comments="Source Table: client_event") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.388+02:00", comments="Source Table: client_event") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.388+02:00", comments="Source Table: client_event") @InsertProvider(type=SqlProviderAdapter.class, method="insert") int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.388+02:00", comments="Source Table: client_event") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -57,7 +57,7 @@ public interface ClientEventRecordMapper { }) ClientEventRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.389+02:00", comments="Source Table: client_event") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -70,22 +70,22 @@ public interface ClientEventRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.389+02:00", comments="Source Table: client_event") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.389+02:00", comments="Source Table: client_event") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(clientEventRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.389+02:00", comments="Source Table: client_event") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, clientEventRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.389+02:00", comments="Source Table: client_event") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, clientEventRecord) .where(id, isEqualTo(id_)) @@ -93,7 +93,7 @@ public interface ClientEventRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.153+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.389+02:00", comments="Source Table: client_event") default int insert(ClientEventRecord record) { return insert(SqlBuilder.insert(record) .into(clientEventRecord) @@ -108,7 +108,7 @@ public interface ClientEventRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.157+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.389+02:00", comments="Source Table: client_event") default int insertSelective(ClientEventRecord record) { return insert(SqlBuilder.insert(record) .into(clientEventRecord) @@ -123,19 +123,19 @@ public interface ClientEventRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.157+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.389+02:00", comments="Source Table: client_event") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, clientConnectionId, type, clientTime, serverTime, numericValue, text) .from(clientEventRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.157+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.389+02:00", comments="Source Table: client_event") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, clientConnectionId, type, clientTime, serverTime, numericValue, text) .from(clientEventRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.157+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.389+02:00", comments="Source Table: client_event") default ClientEventRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, clientConnectionId, type, clientTime, serverTime, numericValue, text) .from(clientEventRecord) @@ -144,7 +144,7 @@ public interface ClientEventRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.157+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.389+02:00", comments="Source Table: client_event") default UpdateDSL> updateByExample(ClientEventRecord record) { return UpdateDSL.updateWithMapper(this::update, clientEventRecord) .set(id).equalTo(record::getId) @@ -156,7 +156,7 @@ public interface ClientEventRecordMapper { .set(text).equalTo(record::getText); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.157+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.389+02:00", comments="Source Table: client_event") default UpdateDSL> updateByExampleSelective(ClientEventRecord record) { return UpdateDSL.updateWithMapper(this::update, clientEventRecord) .set(id).equalToWhenPresent(record::getId) @@ -168,7 +168,7 @@ public interface ClientEventRecordMapper { .set(text).equalToWhenPresent(record::getText); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.157+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.389+02:00", comments="Source Table: client_event") default int updateByPrimaryKey(ClientEventRecord record) { return UpdateDSL.updateWithMapper(this::update, clientEventRecord) .set(clientConnectionId).equalTo(record::getClientConnectionId) @@ -182,7 +182,7 @@ public interface ClientEventRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.157+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.389+02:00", comments="Source Table: client_event") default int updateByPrimaryKeySelective(ClientEventRecord record) { return UpdateDSL.updateWithMapper(this::update, clientEventRecord) .set(clientConnectionId).equalToWhenPresent(record::getClientConnectionId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientIndicatorRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientIndicatorRecordDynamicSqlSupport.java index e9af7c0e..d0756da7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientIndicatorRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientIndicatorRecordDynamicSqlSupport.java @@ -6,22 +6,22 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class ClientIndicatorRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source Table: client_indicator") public static final ClientIndicatorRecord clientIndicatorRecord = new ClientIndicatorRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source field: client_indicator.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source field: client_indicator.id") public static final SqlColumn id = clientIndicatorRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source field: client_indicator.client_connection_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source field: client_indicator.client_connection_id") public static final SqlColumn clientConnectionId = clientIndicatorRecord.clientConnectionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source field: client_indicator.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source field: client_indicator.type") public static final SqlColumn type = clientIndicatorRecord.type; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source field: client_indicator.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source field: client_indicator.value") public static final SqlColumn value = clientIndicatorRecord.value; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source Table: client_indicator") public static final class ClientIndicatorRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientIndicatorRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientIndicatorRecordMapper.java index d4d7c1c9..da7cd95c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientIndicatorRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientIndicatorRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface ClientIndicatorRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source Table: client_indicator") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source Table: client_indicator") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source Table: client_indicator") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source Table: client_indicator") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -55,7 +55,7 @@ public interface ClientIndicatorRecordMapper { }) ClientIndicatorRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source Table: client_indicator") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -65,22 +65,22 @@ public interface ClientIndicatorRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source Table: client_indicator") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source Table: client_indicator") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(clientIndicatorRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source Table: client_indicator") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, clientIndicatorRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.458+02:00", comments="Source Table: client_indicator") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, clientIndicatorRecord) .where(id, isEqualTo(id_)) @@ -88,7 +88,7 @@ public interface ClientIndicatorRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.458+02:00", comments="Source Table: client_indicator") default int insert(ClientIndicatorRecord record) { return insert(SqlBuilder.insert(record) .into(clientIndicatorRecord) @@ -99,7 +99,7 @@ public interface ClientIndicatorRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.458+02:00", comments="Source Table: client_indicator") default int insertSelective(ClientIndicatorRecord record) { return insert(SqlBuilder.insert(record) .into(clientIndicatorRecord) @@ -110,19 +110,19 @@ public interface ClientIndicatorRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.458+02:00", comments="Source Table: client_indicator") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, clientConnectionId, type, value) .from(clientIndicatorRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.458+02:00", comments="Source Table: client_indicator") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, clientConnectionId, type, value) .from(clientIndicatorRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.458+02:00", comments="Source Table: client_indicator") default ClientIndicatorRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, clientConnectionId, type, value) .from(clientIndicatorRecord) @@ -131,7 +131,7 @@ public interface ClientIndicatorRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.458+02:00", comments="Source Table: client_indicator") default UpdateDSL> updateByExample(ClientIndicatorRecord record) { return UpdateDSL.updateWithMapper(this::update, clientIndicatorRecord) .set(clientConnectionId).equalTo(record::getClientConnectionId) @@ -139,7 +139,7 @@ public interface ClientIndicatorRecordMapper { .set(value).equalTo(record::getValue); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.458+02:00", comments="Source Table: client_indicator") default UpdateDSL> updateByExampleSelective(ClientIndicatorRecord record) { return UpdateDSL.updateWithMapper(this::update, clientIndicatorRecord) .set(clientConnectionId).equalToWhenPresent(record::getClientConnectionId) @@ -147,7 +147,7 @@ public interface ClientIndicatorRecordMapper { .set(value).equalToWhenPresent(record::getValue); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.458+02:00", comments="Source Table: client_indicator") default int updateByPrimaryKey(ClientIndicatorRecord record) { return UpdateDSL.updateWithMapper(this::update, clientIndicatorRecord) .set(clientConnectionId).equalTo(record::getClientConnectionId) @@ -158,7 +158,7 @@ public interface ClientIndicatorRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.458+02:00", comments="Source Table: client_indicator") default int updateByPrimaryKeySelective(ClientIndicatorRecord record) { return UpdateDSL.updateWithMapper(this::update, clientIndicatorRecord) .set(clientConnectionId).equalToWhenPresent(record::getClientConnectionId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientInstructionRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientInstructionRecordDynamicSqlSupport.java index 417c337f..c3061eba 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientInstructionRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientInstructionRecordDynamicSqlSupport.java @@ -6,31 +6,31 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class ClientInstructionRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source Table: client_instruction") public static final ClientInstructionRecord clientInstructionRecord = new ClientInstructionRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source field: client_instruction.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.392+02:00", comments="Source field: client_instruction.id") public static final SqlColumn id = clientInstructionRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source field: client_instruction.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.392+02:00", comments="Source field: client_instruction.exam_id") public static final SqlColumn examId = clientInstructionRecord.examId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source field: client_instruction.connection_token") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.392+02:00", comments="Source field: client_instruction.connection_token") public static final SqlColumn connectionToken = clientInstructionRecord.connectionToken; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source field: client_instruction.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.392+02:00", comments="Source field: client_instruction.type") public static final SqlColumn type = clientInstructionRecord.type; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source field: client_instruction.attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.393+02:00", comments="Source field: client_instruction.attributes") public static final SqlColumn attributes = clientInstructionRecord.attributes; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source field: client_instruction.needs_confirmation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.393+02:00", comments="Source field: client_instruction.needs_confirmation") public static final SqlColumn needsConfirmation = clientInstructionRecord.needsConfirmation; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source field: client_instruction.timestamp") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.393+02:00", comments="Source field: client_instruction.timestamp") public static final SqlColumn timestamp = clientInstructionRecord.timestamp; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.392+02:00", comments="Source Table: client_instruction") public static final class ClientInstructionRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientInstructionRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientInstructionRecordMapper.java index 3e3c23be..0e6111c8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientInstructionRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientInstructionRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface ClientInstructionRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.393+02:00", comments="Source Table: client_instruction") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.393+02:00", comments="Source Table: client_instruction") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.393+02:00", comments="Source Table: client_instruction") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.393+02:00", comments="Source Table: client_instruction") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -58,7 +58,7 @@ public interface ClientInstructionRecordMapper { }) ClientInstructionRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.393+02:00", comments="Source Table: client_instruction") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -71,22 +71,22 @@ public interface ClientInstructionRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.394+02:00", comments="Source Table: client_instruction") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.394+02:00", comments="Source Table: client_instruction") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(clientInstructionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.394+02:00", comments="Source Table: client_instruction") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, clientInstructionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.394+02:00", comments="Source Table: client_instruction") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, clientInstructionRecord) .where(id, isEqualTo(id_)) @@ -94,7 +94,7 @@ public interface ClientInstructionRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.394+02:00", comments="Source Table: client_instruction") default int insert(ClientInstructionRecord record) { return insert(SqlBuilder.insert(record) .into(clientInstructionRecord) @@ -108,7 +108,7 @@ public interface ClientInstructionRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.395+02:00", comments="Source Table: client_instruction") default int insertSelective(ClientInstructionRecord record) { return insert(SqlBuilder.insert(record) .into(clientInstructionRecord) @@ -122,19 +122,19 @@ public interface ClientInstructionRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.395+02:00", comments="Source Table: client_instruction") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, examId, connectionToken, type, attributes, needsConfirmation, timestamp) .from(clientInstructionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.395+02:00", comments="Source Table: client_instruction") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, examId, connectionToken, type, attributes, needsConfirmation, timestamp) .from(clientInstructionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.395+02:00", comments="Source Table: client_instruction") default ClientInstructionRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, examId, connectionToken, type, attributes, needsConfirmation, timestamp) .from(clientInstructionRecord) @@ -143,7 +143,7 @@ public interface ClientInstructionRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.395+02:00", comments="Source Table: client_instruction") default UpdateDSL> updateByExample(ClientInstructionRecord record) { return UpdateDSL.updateWithMapper(this::update, clientInstructionRecord) .set(examId).equalTo(record::getExamId) @@ -154,7 +154,7 @@ public interface ClientInstructionRecordMapper { .set(timestamp).equalTo(record::getTimestamp); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.395+02:00", comments="Source Table: client_instruction") default UpdateDSL> updateByExampleSelective(ClientInstructionRecord record) { return UpdateDSL.updateWithMapper(this::update, clientInstructionRecord) .set(examId).equalToWhenPresent(record::getExamId) @@ -165,7 +165,7 @@ public interface ClientInstructionRecordMapper { .set(timestamp).equalToWhenPresent(record::getTimestamp); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.162+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.395+02:00", comments="Source Table: client_instruction") default int updateByPrimaryKey(ClientInstructionRecord record) { return UpdateDSL.updateWithMapper(this::update, clientInstructionRecord) .set(examId).equalTo(record::getExamId) @@ -179,7 +179,7 @@ public interface ClientInstructionRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.165+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.395+02:00", comments="Source Table: client_instruction") default int updateByPrimaryKeySelective(ClientInstructionRecord record) { return UpdateDSL.updateWithMapper(this::update, clientInstructionRecord) .set(examId).equalToWhenPresent(record::getExamId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientNotificationRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientNotificationRecordDynamicSqlSupport.java index 6443cef9..88178df1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientNotificationRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientNotificationRecordDynamicSqlSupport.java @@ -6,28 +6,28 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class ClientNotificationRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source Table: client_notification") public static final ClientNotificationRecord clientNotificationRecord = new ClientNotificationRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source field: client_notification.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source field: client_notification.id") public static final SqlColumn id = clientNotificationRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source field: client_notification.client_connection_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source field: client_notification.client_connection_id") public static final SqlColumn clientConnectionId = clientNotificationRecord.clientConnectionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source field: client_notification.event_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source field: client_notification.event_type") public static final SqlColumn eventType = clientNotificationRecord.eventType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source field: client_notification.notification_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source field: client_notification.notification_type") public static final SqlColumn notificationType = clientNotificationRecord.notificationType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source field: client_notification.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source field: client_notification.value") public static final SqlColumn value = clientNotificationRecord.value; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source field: client_notification.text") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source field: client_notification.text") public static final SqlColumn text = clientNotificationRecord.text; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source Table: client_notification") public static final class ClientNotificationRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientNotificationRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientNotificationRecordMapper.java index 6843212d..d6a68494 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientNotificationRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ClientNotificationRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface ClientNotificationRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source Table: client_notification") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source Table: client_notification") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source Table: client_notification") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source Table: client_notification") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -57,7 +57,7 @@ public interface ClientNotificationRecordMapper { }) ClientNotificationRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source Table: client_notification") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -69,22 +69,22 @@ public interface ClientNotificationRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source Table: client_notification") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source Table: client_notification") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(clientNotificationRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.223+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source Table: client_notification") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, clientNotificationRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.224+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source Table: client_notification") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, clientNotificationRecord) .where(id, isEqualTo(id_)) @@ -92,7 +92,7 @@ public interface ClientNotificationRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.224+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.460+02:00", comments="Source Table: client_notification") default int insert(ClientNotificationRecord record) { return insert(SqlBuilder.insert(record) .into(clientNotificationRecord) @@ -105,7 +105,7 @@ public interface ClientNotificationRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.224+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.461+02:00", comments="Source Table: client_notification") default int insertSelective(ClientNotificationRecord record) { return insert(SqlBuilder.insert(record) .into(clientNotificationRecord) @@ -118,19 +118,19 @@ public interface ClientNotificationRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.224+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.461+02:00", comments="Source Table: client_notification") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, clientConnectionId, eventType, notificationType, value, text) .from(clientNotificationRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.224+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.461+02:00", comments="Source Table: client_notification") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, clientConnectionId, eventType, notificationType, value, text) .from(clientNotificationRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.224+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.461+02:00", comments="Source Table: client_notification") default ClientNotificationRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, clientConnectionId, eventType, notificationType, value, text) .from(clientNotificationRecord) @@ -139,7 +139,7 @@ public interface ClientNotificationRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.224+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.461+02:00", comments="Source Table: client_notification") default UpdateDSL> updateByExample(ClientNotificationRecord record) { return UpdateDSL.updateWithMapper(this::update, clientNotificationRecord) .set(clientConnectionId).equalTo(record::getClientConnectionId) @@ -149,7 +149,7 @@ public interface ClientNotificationRecordMapper { .set(text).equalTo(record::getText); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.224+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.461+02:00", comments="Source Table: client_notification") default UpdateDSL> updateByExampleSelective(ClientNotificationRecord record) { return UpdateDSL.updateWithMapper(this::update, clientNotificationRecord) .set(clientConnectionId).equalToWhenPresent(record::getClientConnectionId) @@ -159,7 +159,7 @@ public interface ClientNotificationRecordMapper { .set(text).equalToWhenPresent(record::getText); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.224+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.461+02:00", comments="Source Table: client_notification") default int updateByPrimaryKey(ClientNotificationRecord record) { return UpdateDSL.updateWithMapper(this::update, clientNotificationRecord) .set(clientConnectionId).equalTo(record::getClientConnectionId) @@ -172,7 +172,7 @@ public interface ClientNotificationRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.224+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.461+02:00", comments="Source Table: client_notification") default int updateByPrimaryKeySelective(ClientNotificationRecord record) { return UpdateDSL.updateWithMapper(this::update, clientNotificationRecord) .set(clientConnectionId).equalToWhenPresent(record::getClientConnectionId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationAttributeRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationAttributeRecordDynamicSqlSupport.java index 8aaab2c6..2e71975f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationAttributeRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationAttributeRecordDynamicSqlSupport.java @@ -6,34 +6,34 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class ConfigurationAttributeRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.942+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.155+02:00", comments="Source Table: configuration_attribute") public static final ConfigurationAttributeRecord configurationAttributeRecord = new ConfigurationAttributeRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.944+01:00", comments="Source field: configuration_attribute.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.157+02:00", comments="Source field: configuration_attribute.id") public static final SqlColumn id = configurationAttributeRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.945+01:00", comments="Source field: configuration_attribute.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.157+02:00", comments="Source field: configuration_attribute.name") public static final SqlColumn name = configurationAttributeRecord.name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.945+01:00", comments="Source field: configuration_attribute.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.158+02:00", comments="Source field: configuration_attribute.type") public static final SqlColumn type = configurationAttributeRecord.type; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.945+01:00", comments="Source field: configuration_attribute.parent_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.158+02:00", comments="Source field: configuration_attribute.parent_id") public static final SqlColumn parentId = configurationAttributeRecord.parentId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.945+01:00", comments="Source field: configuration_attribute.resources") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.158+02:00", comments="Source field: configuration_attribute.resources") public static final SqlColumn resources = configurationAttributeRecord.resources; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.945+01:00", comments="Source field: configuration_attribute.validator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.158+02:00", comments="Source field: configuration_attribute.validator") public static final SqlColumn validator = configurationAttributeRecord.validator; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.945+01:00", comments="Source field: configuration_attribute.dependencies") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.158+02:00", comments="Source field: configuration_attribute.dependencies") public static final SqlColumn dependencies = configurationAttributeRecord.dependencies; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.945+01:00", comments="Source field: configuration_attribute.default_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.158+02:00", comments="Source field: configuration_attribute.default_value") public static final SqlColumn defaultValue = configurationAttributeRecord.defaultValue; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.944+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.156+02:00", comments="Source Table: configuration_attribute") public static final class ConfigurationAttributeRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationAttributeRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationAttributeRecordMapper.java index 87c5cdb8..99483c68 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationAttributeRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationAttributeRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface ConfigurationAttributeRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.946+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.159+02:00", comments="Source Table: configuration_attribute") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.949+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.160+02:00", comments="Source Table: configuration_attribute") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.949+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.161+02:00", comments="Source Table: configuration_attribute") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.951+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.162+02:00", comments="Source Table: configuration_attribute") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -59,7 +59,7 @@ public interface ConfigurationAttributeRecordMapper { }) ConfigurationAttributeRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.952+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.163+02:00", comments="Source Table: configuration_attribute") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -73,22 +73,22 @@ public interface ConfigurationAttributeRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.953+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.164+02:00", comments="Source Table: configuration_attribute") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.953+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.164+02:00", comments="Source Table: configuration_attribute") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(configurationAttributeRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.954+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.165+02:00", comments="Source Table: configuration_attribute") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, configurationAttributeRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.954+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.166+02:00", comments="Source Table: configuration_attribute") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, configurationAttributeRecord) .where(id, isEqualTo(id_)) @@ -96,7 +96,7 @@ public interface ConfigurationAttributeRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.955+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.167+02:00", comments="Source Table: configuration_attribute") default int insert(ConfigurationAttributeRecord record) { return insert(SqlBuilder.insert(record) .into(configurationAttributeRecord) @@ -111,7 +111,7 @@ public interface ConfigurationAttributeRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.956+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.168+02:00", comments="Source Table: configuration_attribute") default int insertSelective(ConfigurationAttributeRecord record) { return insert(SqlBuilder.insert(record) .into(configurationAttributeRecord) @@ -126,19 +126,19 @@ public interface ConfigurationAttributeRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.957+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.169+02:00", comments="Source Table: configuration_attribute") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, name, type, parentId, resources, validator, dependencies, defaultValue) .from(configurationAttributeRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.958+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.169+02:00", comments="Source Table: configuration_attribute") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, name, type, parentId, resources, validator, dependencies, defaultValue) .from(configurationAttributeRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.959+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.170+02:00", comments="Source Table: configuration_attribute") default ConfigurationAttributeRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, name, type, parentId, resources, validator, dependencies, defaultValue) .from(configurationAttributeRecord) @@ -147,7 +147,7 @@ public interface ConfigurationAttributeRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.960+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.171+02:00", comments="Source Table: configuration_attribute") default UpdateDSL> updateByExample(ConfigurationAttributeRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationAttributeRecord) .set(name).equalTo(record::getName) @@ -159,7 +159,7 @@ public interface ConfigurationAttributeRecordMapper { .set(defaultValue).equalTo(record::getDefaultValue); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.961+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.172+02:00", comments="Source Table: configuration_attribute") default UpdateDSL> updateByExampleSelective(ConfigurationAttributeRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationAttributeRecord) .set(name).equalToWhenPresent(record::getName) @@ -171,7 +171,7 @@ public interface ConfigurationAttributeRecordMapper { .set(defaultValue).equalToWhenPresent(record::getDefaultValue); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.962+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.172+02:00", comments="Source Table: configuration_attribute") default int updateByPrimaryKey(ConfigurationAttributeRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationAttributeRecord) .set(name).equalTo(record::getName) @@ -186,7 +186,7 @@ public interface ConfigurationAttributeRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.963+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.173+02:00", comments="Source Table: configuration_attribute") default int updateByPrimaryKeySelective(ConfigurationAttributeRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationAttributeRecord) .set(name).equalToWhenPresent(record::getName) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationNodeRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationNodeRecordDynamicSqlSupport.java index 7c10d2f4..2927c906 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationNodeRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationNodeRecordDynamicSqlSupport.java @@ -6,34 +6,40 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class ConfigurationNodeRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.119+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.344+02:00", comments="Source Table: configuration_node") public static final ConfigurationNodeRecord configurationNodeRecord = new ConfigurationNodeRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.119+01:00", comments="Source field: configuration_node.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.344+02:00", comments="Source field: configuration_node.id") public static final SqlColumn id = configurationNodeRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.120+01:00", comments="Source field: configuration_node.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.344+02:00", comments="Source field: configuration_node.institution_id") public static final SqlColumn institutionId = configurationNodeRecord.institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.120+01:00", comments="Source field: configuration_node.template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.344+02:00", comments="Source field: configuration_node.template_id") public static final SqlColumn templateId = configurationNodeRecord.templateId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.120+01:00", comments="Source field: configuration_node.owner") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.345+02:00", comments="Source field: configuration_node.owner") public static final SqlColumn owner = configurationNodeRecord.owner; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.120+01:00", comments="Source field: configuration_node.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.345+02:00", comments="Source field: configuration_node.name") public static final SqlColumn name = configurationNodeRecord.name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.120+01:00", comments="Source field: configuration_node.description") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.345+02:00", comments="Source field: configuration_node.description") public static final SqlColumn description = configurationNodeRecord.description; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.120+01:00", comments="Source field: configuration_node.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.345+02:00", comments="Source field: configuration_node.type") public static final SqlColumn type = configurationNodeRecord.type; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.120+01:00", comments="Source field: configuration_node.status") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.345+02:00", comments="Source field: configuration_node.status") public static final SqlColumn status = configurationNodeRecord.status; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.119+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.345+02:00", comments="Source field: configuration_node.last_update_time") + public static final SqlColumn lastUpdateTime = configurationNodeRecord.lastUpdateTime; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.346+02:00", comments="Source field: configuration_node.last_update_user") + public static final SqlColumn lastUpdateUser = configurationNodeRecord.lastUpdateUser; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.344+02:00", comments="Source Table: configuration_node") public static final class ConfigurationNodeRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); @@ -51,6 +57,10 @@ public final class ConfigurationNodeRecordDynamicSqlSupport { public final SqlColumn status = column("status", JDBCType.VARCHAR); + public final SqlColumn lastUpdateTime = column("last_update_time", JDBCType.BIGINT); + + public final SqlColumn lastUpdateUser = column("last_update_user", JDBCType.VARCHAR); + public ConfigurationNodeRecord() { super("configuration_node"); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationNodeRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationNodeRecordMapper.java index ee81dc83..d9450147 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationNodeRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationNodeRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface ConfigurationNodeRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.120+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.346+02:00", comments="Source Table: configuration_node") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.120+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.346+02:00", comments="Source Table: configuration_node") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.120+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.346+02:00", comments="Source Table: configuration_node") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.120+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.346+02:00", comments="Source Table: configuration_node") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -55,11 +55,13 @@ public interface ConfigurationNodeRecordMapper { @Arg(column="name", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="description", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="type", javaType=String.class, jdbcType=JdbcType.VARCHAR), - @Arg(column="status", javaType=String.class, jdbcType=JdbcType.VARCHAR) + @Arg(column="status", javaType=String.class, jdbcType=JdbcType.VARCHAR), + @Arg(column="last_update_time", javaType=Long.class, jdbcType=JdbcType.BIGINT), + @Arg(column="last_update_user", javaType=String.class, jdbcType=JdbcType.VARCHAR) }) ConfigurationNodeRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.120+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.346+02:00", comments="Source Table: configuration_node") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -69,26 +71,28 @@ public interface ConfigurationNodeRecordMapper { @Arg(column="name", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="description", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="type", javaType=String.class, jdbcType=JdbcType.VARCHAR), - @Arg(column="status", javaType=String.class, jdbcType=JdbcType.VARCHAR) + @Arg(column="status", javaType=String.class, jdbcType=JdbcType.VARCHAR), + @Arg(column="last_update_time", javaType=Long.class, jdbcType=JdbcType.BIGINT), + @Arg(column="last_update_user", javaType=String.class, jdbcType=JdbcType.VARCHAR) }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.121+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.346+02:00", comments="Source Table: configuration_node") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.121+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.346+02:00", comments="Source Table: configuration_node") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(configurationNodeRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.121+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.346+02:00", comments="Source Table: configuration_node") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, configurationNodeRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.121+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.346+02:00", comments="Source Table: configuration_node") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, configurationNodeRecord) .where(id, isEqualTo(id_)) @@ -96,7 +100,7 @@ public interface ConfigurationNodeRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.121+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.346+02:00", comments="Source Table: configuration_node") default int insert(ConfigurationNodeRecord record) { return insert(SqlBuilder.insert(record) .into(configurationNodeRecord) @@ -107,11 +111,13 @@ public interface ConfigurationNodeRecordMapper { .map(description).toProperty("description") .map(type).toProperty("type") .map(status).toProperty("status") + .map(lastUpdateTime).toProperty("lastUpdateTime") + .map(lastUpdateUser).toProperty("lastUpdateUser") .build() .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.121+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.346+02:00", comments="Source Table: configuration_node") default int insertSelective(ConfigurationNodeRecord record) { return insert(SqlBuilder.insert(record) .into(configurationNodeRecord) @@ -122,32 +128,34 @@ public interface ConfigurationNodeRecordMapper { .map(description).toPropertyWhenPresent("description", record::getDescription) .map(type).toPropertyWhenPresent("type", record::getType) .map(status).toPropertyWhenPresent("status", record::getStatus) + .map(lastUpdateTime).toPropertyWhenPresent("lastUpdateTime", record::getLastUpdateTime) + .map(lastUpdateUser).toPropertyWhenPresent("lastUpdateUser", record::getLastUpdateUser) .build() .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.121+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.347+02:00", comments="Source Table: configuration_node") default QueryExpressionDSL>> selectByExample() { - return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, templateId, owner, name, description, type, status) + return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, templateId, owner, name, description, type, status, lastUpdateTime, lastUpdateUser) .from(configurationNodeRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.121+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.347+02:00", comments="Source Table: configuration_node") default QueryExpressionDSL>> selectDistinctByExample() { - return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, templateId, owner, name, description, type, status) + return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, templateId, owner, name, description, type, status, lastUpdateTime, lastUpdateUser) .from(configurationNodeRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.121+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.347+02:00", comments="Source Table: configuration_node") default ConfigurationNodeRecord selectByPrimaryKey(Long id_) { - return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, templateId, owner, name, description, type, status) + return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, templateId, owner, name, description, type, status, lastUpdateTime, lastUpdateUser) .from(configurationNodeRecord) .where(id, isEqualTo(id_)) .build() .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.121+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.347+02:00", comments="Source Table: configuration_node") default UpdateDSL> updateByExample(ConfigurationNodeRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationNodeRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -156,10 +164,12 @@ public interface ConfigurationNodeRecordMapper { .set(name).equalTo(record::getName) .set(description).equalTo(record::getDescription) .set(type).equalTo(record::getType) - .set(status).equalTo(record::getStatus); + .set(status).equalTo(record::getStatus) + .set(lastUpdateTime).equalTo(record::getLastUpdateTime) + .set(lastUpdateUser).equalTo(record::getLastUpdateUser); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.121+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.347+02:00", comments="Source Table: configuration_node") default UpdateDSL> updateByExampleSelective(ConfigurationNodeRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationNodeRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) @@ -168,10 +178,12 @@ public interface ConfigurationNodeRecordMapper { .set(name).equalToWhenPresent(record::getName) .set(description).equalToWhenPresent(record::getDescription) .set(type).equalToWhenPresent(record::getType) - .set(status).equalToWhenPresent(record::getStatus); + .set(status).equalToWhenPresent(record::getStatus) + .set(lastUpdateTime).equalToWhenPresent(record::getLastUpdateTime) + .set(lastUpdateUser).equalToWhenPresent(record::getLastUpdateUser); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.121+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.347+02:00", comments="Source Table: configuration_node") default int updateByPrimaryKey(ConfigurationNodeRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationNodeRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -181,12 +193,14 @@ public interface ConfigurationNodeRecordMapper { .set(description).equalTo(record::getDescription) .set(type).equalTo(record::getType) .set(status).equalTo(record::getStatus) + .set(lastUpdateTime).equalTo(record::getLastUpdateTime) + .set(lastUpdateUser).equalTo(record::getLastUpdateUser) .where(id, isEqualTo(record::getId)) .build() .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.121+01:00", comments="Source Table: configuration_node") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.347+02:00", comments="Source Table: configuration_node") default int updateByPrimaryKeySelective(ConfigurationNodeRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationNodeRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) @@ -196,6 +210,8 @@ public interface ConfigurationNodeRecordMapper { .set(description).equalToWhenPresent(record::getDescription) .set(type).equalToWhenPresent(record::getType) .set(status).equalToWhenPresent(record::getStatus) + .set(lastUpdateTime).equalToWhenPresent(record::getLastUpdateTime) + .set(lastUpdateUser).equalToWhenPresent(record::getLastUpdateUser) .where(id, isEqualTo(record::getId)) .build() .execute(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationRecordDynamicSqlSupport.java index 71f637e4..5c8f3337 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationRecordDynamicSqlSupport.java @@ -7,28 +7,28 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class ConfigurationRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.112+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.338+02:00", comments="Source Table: configuration") public static final ConfigurationRecord configurationRecord = new ConfigurationRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.112+01:00", comments="Source field: configuration.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.338+02:00", comments="Source field: configuration.id") public static final SqlColumn id = configurationRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.112+01:00", comments="Source field: configuration.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.338+02:00", comments="Source field: configuration.institution_id") public static final SqlColumn institutionId = configurationRecord.institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.113+01:00", comments="Source field: configuration.configuration_node_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.338+02:00", comments="Source field: configuration.configuration_node_id") public static final SqlColumn configurationNodeId = configurationRecord.configurationNodeId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.113+01:00", comments="Source field: configuration.version") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.339+02:00", comments="Source field: configuration.version") public static final SqlColumn version = configurationRecord.version; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.113+01:00", comments="Source field: configuration.version_date") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.339+02:00", comments="Source field: configuration.version_date") public static final SqlColumn versionDate = configurationRecord.versionDate; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.113+01:00", comments="Source field: configuration.followup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.339+02:00", comments="Source field: configuration.followup") public static final SqlColumn followup = configurationRecord.followup; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.112+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.338+02:00", comments="Source Table: configuration") public static final class ConfigurationRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationRecordMapper.java index 0f6be216..ddc6a19e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationRecordMapper.java @@ -34,20 +34,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface ConfigurationRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.113+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.339+02:00", comments="Source Table: configuration") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.113+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.339+02:00", comments="Source Table: configuration") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.113+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.339+02:00", comments="Source Table: configuration") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.113+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.339+02:00", comments="Source Table: configuration") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -59,7 +59,7 @@ public interface ConfigurationRecordMapper { }) ConfigurationRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.113+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.340+02:00", comments="Source Table: configuration") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -71,22 +71,22 @@ public interface ConfigurationRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.114+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.340+02:00", comments="Source Table: configuration") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.114+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.340+02:00", comments="Source Table: configuration") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(configurationRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.114+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.340+02:00", comments="Source Table: configuration") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, configurationRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.114+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.340+02:00", comments="Source Table: configuration") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, configurationRecord) .where(id, isEqualTo(id_)) @@ -94,7 +94,7 @@ public interface ConfigurationRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.114+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.340+02:00", comments="Source Table: configuration") default int insert(ConfigurationRecord record) { return insert(SqlBuilder.insert(record) .into(configurationRecord) @@ -107,7 +107,7 @@ public interface ConfigurationRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.115+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.340+02:00", comments="Source Table: configuration") default int insertSelective(ConfigurationRecord record) { return insert(SqlBuilder.insert(record) .into(configurationRecord) @@ -120,19 +120,19 @@ public interface ConfigurationRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.115+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.340+02:00", comments="Source Table: configuration") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, configurationNodeId, version, versionDate, followup) .from(configurationRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.115+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.340+02:00", comments="Source Table: configuration") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, configurationNodeId, version, versionDate, followup) .from(configurationRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.115+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.340+02:00", comments="Source Table: configuration") default ConfigurationRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, configurationNodeId, version, versionDate, followup) .from(configurationRecord) @@ -141,7 +141,7 @@ public interface ConfigurationRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.115+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.341+02:00", comments="Source Table: configuration") default UpdateDSL> updateByExample(ConfigurationRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -151,7 +151,7 @@ public interface ConfigurationRecordMapper { .set(followup).equalTo(record::getFollowup); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.115+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.341+02:00", comments="Source Table: configuration") default UpdateDSL> updateByExampleSelective(ConfigurationRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) @@ -161,7 +161,7 @@ public interface ConfigurationRecordMapper { .set(followup).equalToWhenPresent(record::getFollowup); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.115+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.341+02:00", comments="Source Table: configuration") default int updateByPrimaryKey(ConfigurationRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -174,7 +174,7 @@ public interface ConfigurationRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.115+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.341+02:00", comments="Source Table: configuration") default int updateByPrimaryKeySelective(ConfigurationRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationValueRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationValueRecordDynamicSqlSupport.java index 0f4c4e53..b2c4b872 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationValueRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationValueRecordDynamicSqlSupport.java @@ -6,28 +6,28 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class ConfigurationValueRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.095+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.319+02:00", comments="Source Table: configuration_value") public static final ConfigurationValueRecord configurationValueRecord = new ConfigurationValueRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.095+01:00", comments="Source field: configuration_value.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.320+02:00", comments="Source field: configuration_value.id") public static final SqlColumn id = configurationValueRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.095+01:00", comments="Source field: configuration_value.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.320+02:00", comments="Source field: configuration_value.institution_id") public static final SqlColumn institutionId = configurationValueRecord.institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.096+01:00", comments="Source field: configuration_value.configuration_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.320+02:00", comments="Source field: configuration_value.configuration_id") public static final SqlColumn configurationId = configurationValueRecord.configurationId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.096+01:00", comments="Source field: configuration_value.configuration_attribute_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.320+02:00", comments="Source field: configuration_value.configuration_attribute_id") public static final SqlColumn configurationAttributeId = configurationValueRecord.configurationAttributeId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.096+01:00", comments="Source field: configuration_value.list_index") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.321+02:00", comments="Source field: configuration_value.list_index") public static final SqlColumn listIndex = configurationValueRecord.listIndex; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.096+01:00", comments="Source field: configuration_value.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.321+02:00", comments="Source field: configuration_value.value") public static final SqlColumn value = configurationValueRecord.value; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.095+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.320+02:00", comments="Source Table: configuration_value") public static final class ConfigurationValueRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationValueRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationValueRecordMapper.java index 31b25057..6cea1c4c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationValueRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ConfigurationValueRecordMapper.java @@ -31,19 +31,19 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface ConfigurationValueRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.096+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.321+02:00", comments="Source Table: configuration_value") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.096+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.321+02:00", comments="Source Table: configuration_value") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.096+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.321+02:00", comments="Source Table: configuration_value") @InsertProvider(type=SqlProviderAdapter.class, method="insert") int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.096+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.321+02:00", comments="Source Table: configuration_value") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -55,7 +55,7 @@ public interface ConfigurationValueRecordMapper { }) ConfigurationValueRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.096+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.321+02:00", comments="Source Table: configuration_value") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -67,22 +67,22 @@ public interface ConfigurationValueRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.097+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.322+02:00", comments="Source Table: configuration_value") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.097+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.322+02:00", comments="Source Table: configuration_value") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(configurationValueRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.097+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.322+02:00", comments="Source Table: configuration_value") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, configurationValueRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.097+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.322+02:00", comments="Source Table: configuration_value") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, configurationValueRecord) .where(id, isEqualTo(id_)) @@ -90,7 +90,7 @@ public interface ConfigurationValueRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.097+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.322+02:00", comments="Source Table: configuration_value") default int insert(ConfigurationValueRecord record) { return insert(SqlBuilder.insert(record) .into(configurationValueRecord) @@ -104,7 +104,7 @@ public interface ConfigurationValueRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.097+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.322+02:00", comments="Source Table: configuration_value") default int insertSelective(ConfigurationValueRecord record) { return insert(SqlBuilder.insert(record) .into(configurationValueRecord) @@ -118,19 +118,19 @@ public interface ConfigurationValueRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.097+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.322+02:00", comments="Source Table: configuration_value") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, configurationId, configurationAttributeId, listIndex, value) .from(configurationValueRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.097+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.322+02:00", comments="Source Table: configuration_value") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, configurationId, configurationAttributeId, listIndex, value) .from(configurationValueRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.097+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.322+02:00", comments="Source Table: configuration_value") default ConfigurationValueRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, configurationId, configurationAttributeId, listIndex, value) .from(configurationValueRecord) @@ -139,7 +139,7 @@ public interface ConfigurationValueRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.097+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.322+02:00", comments="Source Table: configuration_value") default UpdateDSL> updateByExample(ConfigurationValueRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationValueRecord) .set(id).equalTo(record::getId) @@ -150,7 +150,7 @@ public interface ConfigurationValueRecordMapper { .set(value).equalTo(record::getValue); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.097+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.323+02:00", comments="Source Table: configuration_value") default UpdateDSL> updateByExampleSelective(ConfigurationValueRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationValueRecord) .set(id).equalToWhenPresent(record::getId) @@ -161,7 +161,7 @@ public interface ConfigurationValueRecordMapper { .set(value).equalToWhenPresent(record::getValue); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.098+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.323+02:00", comments="Source Table: configuration_value") default int updateByPrimaryKey(ConfigurationValueRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationValueRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -174,7 +174,7 @@ public interface ConfigurationValueRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.098+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.323+02:00", comments="Source Table: configuration_value") default int updateByPrimaryKeySelective(ConfigurationValueRecord record) { return UpdateDSL.updateWithMapper(this::update, configurationValueRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamConfigurationMapRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamConfigurationMapRecordDynamicSqlSupport.java index 1b8a5a7a..5eb955cb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamConfigurationMapRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamConfigurationMapRecordDynamicSqlSupport.java @@ -6,28 +6,28 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class ExamConfigurationMapRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.125+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.354+02:00", comments="Source Table: exam_configuration_map") public static final ExamConfigurationMapRecord examConfigurationMapRecord = new ExamConfigurationMapRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.126+01:00", comments="Source field: exam_configuration_map.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.355+02:00", comments="Source field: exam_configuration_map.id") public static final SqlColumn id = examConfigurationMapRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.126+01:00", comments="Source field: exam_configuration_map.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.355+02:00", comments="Source field: exam_configuration_map.institution_id") public static final SqlColumn institutionId = examConfigurationMapRecord.institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.126+01:00", comments="Source field: exam_configuration_map.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.355+02:00", comments="Source field: exam_configuration_map.exam_id") public static final SqlColumn examId = examConfigurationMapRecord.examId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.126+01:00", comments="Source field: exam_configuration_map.configuration_node_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.355+02:00", comments="Source field: exam_configuration_map.configuration_node_id") public static final SqlColumn configurationNodeId = examConfigurationMapRecord.configurationNodeId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.126+01:00", comments="Source field: exam_configuration_map.user_names") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.355+02:00", comments="Source field: exam_configuration_map.user_names") public static final SqlColumn userNames = examConfigurationMapRecord.userNames; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.126+01:00", comments="Source field: exam_configuration_map.encrypt_secret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.355+02:00", comments="Source field: exam_configuration_map.encrypt_secret") public static final SqlColumn encryptSecret = examConfigurationMapRecord.encryptSecret; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.126+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.354+02:00", comments="Source Table: exam_configuration_map") public static final class ExamConfigurationMapRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamConfigurationMapRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamConfigurationMapRecordMapper.java index dfa9bd60..4f65fc02 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamConfigurationMapRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamConfigurationMapRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface ExamConfigurationMapRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.126+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.356+02:00", comments="Source Table: exam_configuration_map") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.126+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.356+02:00", comments="Source Table: exam_configuration_map") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.126+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.356+02:00", comments="Source Table: exam_configuration_map") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.126+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.356+02:00", comments="Source Table: exam_configuration_map") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -57,7 +57,7 @@ public interface ExamConfigurationMapRecordMapper { }) ExamConfigurationMapRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.126+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.356+02:00", comments="Source Table: exam_configuration_map") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -69,22 +69,22 @@ public interface ExamConfigurationMapRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.127+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.356+02:00", comments="Source Table: exam_configuration_map") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.127+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.356+02:00", comments="Source Table: exam_configuration_map") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(examConfigurationMapRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.127+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.356+02:00", comments="Source Table: exam_configuration_map") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, examConfigurationMapRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.127+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.356+02:00", comments="Source Table: exam_configuration_map") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, examConfigurationMapRecord) .where(id, isEqualTo(id_)) @@ -92,7 +92,7 @@ public interface ExamConfigurationMapRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.127+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.356+02:00", comments="Source Table: exam_configuration_map") default int insert(ExamConfigurationMapRecord record) { return insert(SqlBuilder.insert(record) .into(examConfigurationMapRecord) @@ -105,7 +105,7 @@ public interface ExamConfigurationMapRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.127+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.356+02:00", comments="Source Table: exam_configuration_map") default int insertSelective(ExamConfigurationMapRecord record) { return insert(SqlBuilder.insert(record) .into(examConfigurationMapRecord) @@ -118,19 +118,19 @@ public interface ExamConfigurationMapRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.127+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.356+02:00", comments="Source Table: exam_configuration_map") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, examId, configurationNodeId, userNames, encryptSecret) .from(examConfigurationMapRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.127+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.357+02:00", comments="Source Table: exam_configuration_map") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, examId, configurationNodeId, userNames, encryptSecret) .from(examConfigurationMapRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.127+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.358+02:00", comments="Source Table: exam_configuration_map") default ExamConfigurationMapRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, examId, configurationNodeId, userNames, encryptSecret) .from(examConfigurationMapRecord) @@ -139,7 +139,7 @@ public interface ExamConfigurationMapRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.127+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.358+02:00", comments="Source Table: exam_configuration_map") default UpdateDSL> updateByExample(ExamConfigurationMapRecord record) { return UpdateDSL.updateWithMapper(this::update, examConfigurationMapRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -149,7 +149,7 @@ public interface ExamConfigurationMapRecordMapper { .set(encryptSecret).equalTo(record::getEncryptSecret); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.127+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.358+02:00", comments="Source Table: exam_configuration_map") default UpdateDSL> updateByExampleSelective(ExamConfigurationMapRecord record) { return UpdateDSL.updateWithMapper(this::update, examConfigurationMapRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) @@ -159,7 +159,7 @@ public interface ExamConfigurationMapRecordMapper { .set(encryptSecret).equalToWhenPresent(record::getEncryptSecret); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.127+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.358+02:00", comments="Source Table: exam_configuration_map") default int updateByPrimaryKey(ExamConfigurationMapRecord record) { return UpdateDSL.updateWithMapper(this::update, examConfigurationMapRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -172,7 +172,7 @@ public interface ExamConfigurationMapRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.127+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.358+02:00", comments="Source Table: exam_configuration_map") default int updateByPrimaryKeySelective(ExamConfigurationMapRecord record) { return UpdateDSL.updateWithMapper(this::update, examConfigurationMapRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamRecordDynamicSqlSupport.java index 82ea9462..49c013c9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamRecordDynamicSqlSupport.java @@ -2,62 +2,75 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper; import java.sql.JDBCType; import javax.annotation.Generated; +import org.joda.time.DateTime; import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class ExamRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.133+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.361+02:00", comments="Source Table: exam") public static final ExamRecord examRecord = new ExamRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.133+01:00", comments="Source field: exam.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.362+02:00", comments="Source field: exam.id") public static final SqlColumn id = examRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.133+01:00", comments="Source field: exam.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.362+02:00", comments="Source field: exam.institution_id") public static final SqlColumn institutionId = examRecord.institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.133+01:00", comments="Source field: exam.lms_setup_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.362+02:00", comments="Source field: exam.lms_setup_id") public static final SqlColumn lmsSetupId = examRecord.lmsSetupId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.133+01:00", comments="Source field: exam.external_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.362+02:00", comments="Source field: exam.external_id") public static final SqlColumn externalId = examRecord.externalId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.133+01:00", comments="Source field: exam.owner") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.362+02:00", comments="Source field: exam.owner") public static final SqlColumn owner = examRecord.owner; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.133+01:00", comments="Source field: exam.supporter") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.362+02:00", comments="Source field: exam.supporter") public static final SqlColumn supporter = examRecord.supporter; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.133+01:00", comments="Source field: exam.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.362+02:00", comments="Source field: exam.type") public static final SqlColumn type = examRecord.type; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.133+01:00", comments="Source field: exam.quit_password") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.362+02:00", comments="Source field: exam.quit_password") public static final SqlColumn quitPassword = examRecord.quitPassword; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.134+01:00", comments="Source field: exam.browser_keys") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.362+02:00", comments="Source field: exam.browser_keys") public static final SqlColumn browserKeys = examRecord.browserKeys; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.134+01:00", comments="Source field: exam.status") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.363+02:00", comments="Source field: exam.status") public static final SqlColumn status = examRecord.status; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.134+01:00", comments="Source field: exam.lms_seb_restriction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.363+02:00", comments="Source field: exam.lms_seb_restriction") public static final SqlColumn lmsSebRestriction = examRecord.lmsSebRestriction; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.134+01:00", comments="Source field: exam.updating") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.363+02:00", comments="Source field: exam.updating") public static final SqlColumn updating = examRecord.updating; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.134+01:00", comments="Source field: exam.lastupdate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.363+02:00", comments="Source field: exam.lastupdate") public static final SqlColumn lastupdate = examRecord.lastupdate; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.134+01:00", comments="Source field: exam.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.363+02:00", comments="Source field: exam.active") public static final SqlColumn active = examRecord.active; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.134+01:00", comments="Source field: exam.exam_template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.363+02:00", comments="Source field: exam.exam_template_id") public static final SqlColumn examTemplateId = examRecord.examTemplateId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.134+01:00", comments="Source field: exam.last_modified") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.363+02:00", comments="Source field: exam.last_modified") public static final SqlColumn lastModified = examRecord.lastModified; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.133+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.363+02:00", comments="Source field: exam.quiz_name") + public static final SqlColumn quizName = examRecord.quizName; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.363+02:00", comments="Source field: exam.quiz_start_time") + public static final SqlColumn quizStartTime = examRecord.quizStartTime; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.364+02:00", comments="Source field: exam.quiz_end_time") + public static final SqlColumn quizEndTime = examRecord.quizEndTime; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.364+02:00", comments="Source field: exam.lms_available") + public static final SqlColumn lmsAvailable = examRecord.lmsAvailable; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.361+02:00", comments="Source Table: exam") public static final class ExamRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); @@ -91,6 +104,14 @@ public final class ExamRecordDynamicSqlSupport { public final SqlColumn lastModified = column("last_modified", JDBCType.BIGINT); + public final SqlColumn quizName = column("quiz_name", JDBCType.VARCHAR); + + public final SqlColumn quizStartTime = column("quiz_start_time", JDBCType.TIMESTAMP, "ch.ethz.seb.sebserver.webservice.datalayer.batis.JodaTimeTypeResolver"); + + public final SqlColumn quizEndTime = column("quiz_end_time", JDBCType.TIMESTAMP, "ch.ethz.seb.sebserver.webservice.datalayer.batis.JodaTimeTypeResolver"); + + public final SqlColumn lmsAvailable = column("lms_available", JDBCType.INTEGER); + public ExamRecord() { super("exam"); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamRecordMapper.java index 08792e52..faf30edc 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamRecordMapper.java @@ -3,6 +3,7 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper; import static ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordDynamicSqlSupport.*; import static org.mybatis.dynamic.sql.SqlBuilder.*; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.JodaTimeTypeResolver; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ExamRecord; import java.util.List; import javax.annotation.Generated; @@ -15,6 +16,7 @@ import org.apache.ibatis.annotations.SelectKey; import org.apache.ibatis.annotations.SelectProvider; import org.apache.ibatis.annotations.UpdateProvider; import org.apache.ibatis.type.JdbcType; +import org.joda.time.DateTime; import org.mybatis.dynamic.sql.SqlBuilder; import org.mybatis.dynamic.sql.delete.DeleteDSL; import org.mybatis.dynamic.sql.delete.MyBatis3DeleteModelAdapter; @@ -32,20 +34,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface ExamRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.134+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.364+02:00", comments="Source Table: exam") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.364+02:00", comments="Source Table: exam") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.364+02:00", comments="Source Table: exam") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.364+02:00", comments="Source Table: exam") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -63,11 +65,15 @@ public interface ExamRecordMapper { @Arg(column="lastupdate", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="active", javaType=Integer.class, jdbcType=JdbcType.INTEGER), @Arg(column="exam_template_id", javaType=Long.class, jdbcType=JdbcType.BIGINT), - @Arg(column="last_modified", javaType=Long.class, jdbcType=JdbcType.BIGINT) + @Arg(column="last_modified", javaType=Long.class, jdbcType=JdbcType.BIGINT), + @Arg(column="quiz_name", javaType=String.class, jdbcType=JdbcType.VARCHAR), + @Arg(column="quiz_start_time", javaType=DateTime.class, typeHandler=JodaTimeTypeResolver.class, jdbcType=JdbcType.TIMESTAMP), + @Arg(column="quiz_end_time", javaType=DateTime.class, typeHandler=JodaTimeTypeResolver.class, jdbcType=JdbcType.TIMESTAMP), + @Arg(column="lms_available", javaType=Integer.class, jdbcType=JdbcType.INTEGER) }) ExamRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.364+02:00", comments="Source Table: exam") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -85,26 +91,30 @@ public interface ExamRecordMapper { @Arg(column="lastupdate", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="active", javaType=Integer.class, jdbcType=JdbcType.INTEGER), @Arg(column="exam_template_id", javaType=Long.class, jdbcType=JdbcType.BIGINT), - @Arg(column="last_modified", javaType=Long.class, jdbcType=JdbcType.BIGINT) + @Arg(column="last_modified", javaType=Long.class, jdbcType=JdbcType.BIGINT), + @Arg(column="quiz_name", javaType=String.class, jdbcType=JdbcType.VARCHAR), + @Arg(column="quiz_start_time", javaType=DateTime.class, typeHandler=JodaTimeTypeResolver.class, jdbcType=JdbcType.TIMESTAMP), + @Arg(column="quiz_end_time", javaType=DateTime.class, typeHandler=JodaTimeTypeResolver.class, jdbcType=JdbcType.TIMESTAMP), + @Arg(column="lms_available", javaType=Integer.class, jdbcType=JdbcType.INTEGER) }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.364+02:00", comments="Source Table: exam") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.364+02:00", comments="Source Table: exam") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(examRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.364+02:00", comments="Source Table: exam") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, examRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.365+02:00", comments="Source Table: exam") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, examRecord) .where(id, isEqualTo(id_)) @@ -112,7 +122,7 @@ public interface ExamRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.365+02:00", comments="Source Table: exam") default int insert(ExamRecord record) { return insert(SqlBuilder.insert(record) .into(examRecord) @@ -131,11 +141,15 @@ public interface ExamRecordMapper { .map(active).toProperty("active") .map(examTemplateId).toProperty("examTemplateId") .map(lastModified).toProperty("lastModified") + .map(quizName).toProperty("quizName") + .map(quizStartTime).toProperty("quizStartTime") + .map(quizEndTime).toProperty("quizEndTime") + .map(lmsAvailable).toProperty("lmsAvailable") .build() .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.365+02:00", comments="Source Table: exam") default int insertSelective(ExamRecord record) { return insert(SqlBuilder.insert(record) .into(examRecord) @@ -154,32 +168,36 @@ public interface ExamRecordMapper { .map(active).toPropertyWhenPresent("active", record::getActive) .map(examTemplateId).toPropertyWhenPresent("examTemplateId", record::getExamTemplateId) .map(lastModified).toPropertyWhenPresent("lastModified", record::getLastModified) + .map(quizName).toPropertyWhenPresent("quizName", record::getQuizName) + .map(quizStartTime).toPropertyWhenPresent("quizStartTime", record::getQuizStartTime) + .map(quizEndTime).toPropertyWhenPresent("quizEndTime", record::getQuizEndTime) + .map(lmsAvailable).toPropertyWhenPresent("lmsAvailable", record::getLmsAvailable) .build() .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.365+02:00", comments="Source Table: exam") default QueryExpressionDSL>> selectByExample() { - return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, lmsSetupId, externalId, owner, supporter, type, quitPassword, browserKeys, status, lmsSebRestriction, updating, lastupdate, active, examTemplateId, lastModified) + return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, lmsSetupId, externalId, owner, supporter, type, quitPassword, browserKeys, status, lmsSebRestriction, updating, lastupdate, active, examTemplateId, lastModified, quizName, quizStartTime, quizEndTime, lmsAvailable) .from(examRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.365+02:00", comments="Source Table: exam") default QueryExpressionDSL>> selectDistinctByExample() { - return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, lmsSetupId, externalId, owner, supporter, type, quitPassword, browserKeys, status, lmsSebRestriction, updating, lastupdate, active, examTemplateId, lastModified) + return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, lmsSetupId, externalId, owner, supporter, type, quitPassword, browserKeys, status, lmsSebRestriction, updating, lastupdate, active, examTemplateId, lastModified, quizName, quizStartTime, quizEndTime, lmsAvailable) .from(examRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.365+02:00", comments="Source Table: exam") default ExamRecord selectByPrimaryKey(Long id_) { - return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, lmsSetupId, externalId, owner, supporter, type, quitPassword, browserKeys, status, lmsSebRestriction, updating, lastupdate, active, examTemplateId, lastModified) + return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, lmsSetupId, externalId, owner, supporter, type, quitPassword, browserKeys, status, lmsSebRestriction, updating, lastupdate, active, examTemplateId, lastModified, quizName, quizStartTime, quizEndTime, lmsAvailable) .from(examRecord) .where(id, isEqualTo(id_)) .build() .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.365+02:00", comments="Source Table: exam") default UpdateDSL> updateByExample(ExamRecord record) { return UpdateDSL.updateWithMapper(this::update, examRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -196,10 +214,14 @@ public interface ExamRecordMapper { .set(lastupdate).equalTo(record::getLastupdate) .set(active).equalTo(record::getActive) .set(examTemplateId).equalTo(record::getExamTemplateId) - .set(lastModified).equalTo(record::getLastModified); + .set(lastModified).equalTo(record::getLastModified) + .set(quizName).equalTo(record::getQuizName) + .set(quizStartTime).equalTo(record::getQuizStartTime) + .set(quizEndTime).equalTo(record::getQuizEndTime) + .set(lmsAvailable).equalTo(record::getLmsAvailable); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.135+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.365+02:00", comments="Source Table: exam") default UpdateDSL> updateByExampleSelective(ExamRecord record) { return UpdateDSL.updateWithMapper(this::update, examRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) @@ -216,10 +238,14 @@ public interface ExamRecordMapper { .set(lastupdate).equalToWhenPresent(record::getLastupdate) .set(active).equalToWhenPresent(record::getActive) .set(examTemplateId).equalToWhenPresent(record::getExamTemplateId) - .set(lastModified).equalToWhenPresent(record::getLastModified); + .set(lastModified).equalToWhenPresent(record::getLastModified) + .set(quizName).equalToWhenPresent(record::getQuizName) + .set(quizStartTime).equalToWhenPresent(record::getQuizStartTime) + .set(quizEndTime).equalToWhenPresent(record::getQuizEndTime) + .set(lmsAvailable).equalToWhenPresent(record::getLmsAvailable); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.136+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.365+02:00", comments="Source Table: exam") default int updateByPrimaryKey(ExamRecord record) { return UpdateDSL.updateWithMapper(this::update, examRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -237,12 +263,16 @@ public interface ExamRecordMapper { .set(active).equalTo(record::getActive) .set(examTemplateId).equalTo(record::getExamTemplateId) .set(lastModified).equalTo(record::getLastModified) + .set(quizName).equalTo(record::getQuizName) + .set(quizStartTime).equalTo(record::getQuizStartTime) + .set(quizEndTime).equalTo(record::getQuizEndTime) + .set(lmsAvailable).equalTo(record::getLmsAvailable) .where(id, isEqualTo(record::getId)) .build() .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.136+01:00", comments="Source Table: exam") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.366+02:00", comments="Source Table: exam") default int updateByPrimaryKeySelective(ExamRecord record) { return UpdateDSL.updateWithMapper(this::update, examRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) @@ -260,6 +290,10 @@ public interface ExamRecordMapper { .set(active).equalToWhenPresent(record::getActive) .set(examTemplateId).equalToWhenPresent(record::getExamTemplateId) .set(lastModified).equalToWhenPresent(record::getLastModified) + .set(quizName).equalToWhenPresent(record::getQuizName) + .set(quizStartTime).equalToWhenPresent(record::getQuizStartTime) + .set(quizEndTime).equalToWhenPresent(record::getQuizEndTime) + .set(lmsAvailable).equalToWhenPresent(record::getLmsAvailable) .where(id, isEqualTo(record::getId)) .build() .execute(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamTemplateRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamTemplateRecordDynamicSqlSupport.java index 5b2d8aaa..b2ad346c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamTemplateRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamTemplateRecordDynamicSqlSupport.java @@ -6,37 +6,37 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class ExamTemplateRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.451+02:00", comments="Source Table: exam_template") public static final ExamTemplateRecord examTemplateRecord = new ExamTemplateRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source field: exam_template.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.451+02:00", comments="Source field: exam_template.id") public static final SqlColumn id = examTemplateRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source field: exam_template.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.451+02:00", comments="Source field: exam_template.institution_id") public static final SqlColumn institutionId = examTemplateRecord.institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source field: exam_template.configuration_template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.451+02:00", comments="Source field: exam_template.configuration_template_id") public static final SqlColumn configurationTemplateId = examTemplateRecord.configurationTemplateId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source field: exam_template.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.451+02:00", comments="Source field: exam_template.name") public static final SqlColumn name = examTemplateRecord.name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source field: exam_template.description") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.451+02:00", comments="Source field: exam_template.description") public static final SqlColumn description = examTemplateRecord.description; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source field: exam_template.exam_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.451+02:00", comments="Source field: exam_template.exam_type") public static final SqlColumn examType = examTemplateRecord.examType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source field: exam_template.supporter") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.451+02:00", comments="Source field: exam_template.supporter") public static final SqlColumn supporter = examTemplateRecord.supporter; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source field: exam_template.indicator_templates") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.451+02:00", comments="Source field: exam_template.indicator_templates") public static final SqlColumn indicatorTemplates = examTemplateRecord.indicatorTemplates; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source field: exam_template.institutional_default") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.451+02:00", comments="Source field: exam_template.institutional_default") public static final SqlColumn institutionalDefault = examTemplateRecord.institutionalDefault; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.451+02:00", comments="Source Table: exam_template") public static final class ExamTemplateRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamTemplateRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamTemplateRecordMapper.java index d267e846..ba43526f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamTemplateRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ExamTemplateRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface ExamTemplateRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -60,7 +60,7 @@ public interface ExamTemplateRecordMapper { }) ExamTemplateRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -75,22 +75,22 @@ public interface ExamTemplateRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(examTemplateRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.216+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, examTemplateRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, examTemplateRecord) .where(id, isEqualTo(id_)) @@ -98,7 +98,7 @@ public interface ExamTemplateRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") default int insert(ExamTemplateRecord record) { return insert(SqlBuilder.insert(record) .into(examTemplateRecord) @@ -114,7 +114,7 @@ public interface ExamTemplateRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") default int insertSelective(ExamTemplateRecord record) { return insert(SqlBuilder.insert(record) .into(examTemplateRecord) @@ -130,19 +130,19 @@ public interface ExamTemplateRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, configurationTemplateId, name, description, examType, supporter, indicatorTemplates, institutionalDefault) .from(examTemplateRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, configurationTemplateId, name, description, examType, supporter, indicatorTemplates, institutionalDefault) .from(examTemplateRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") default ExamTemplateRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, configurationTemplateId, name, description, examType, supporter, indicatorTemplates, institutionalDefault) .from(examTemplateRecord) @@ -151,7 +151,7 @@ public interface ExamTemplateRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") default UpdateDSL> updateByExample(ExamTemplateRecord record) { return UpdateDSL.updateWithMapper(this::update, examTemplateRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -164,7 +164,7 @@ public interface ExamTemplateRecordMapper { .set(institutionalDefault).equalTo(record::getInstitutionalDefault); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") default UpdateDSL> updateByExampleSelective(ExamTemplateRecord record) { return UpdateDSL.updateWithMapper(this::update, examTemplateRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) @@ -177,7 +177,7 @@ public interface ExamTemplateRecordMapper { .set(institutionalDefault).equalToWhenPresent(record::getInstitutionalDefault); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") default int updateByPrimaryKey(ExamTemplateRecord record) { return UpdateDSL.updateWithMapper(this::update, examTemplateRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -193,7 +193,7 @@ public interface ExamTemplateRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.452+02:00", comments="Source Table: exam_template") default int updateByPrimaryKeySelective(ExamTemplateRecord record) { return UpdateDSL.updateWithMapper(this::update, examTemplateRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/IndicatorRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/IndicatorRecordDynamicSqlSupport.java index 64dd85f6..4e875ea6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/IndicatorRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/IndicatorRecordDynamicSqlSupport.java @@ -6,31 +6,31 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class IndicatorRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source Table: indicator") public static final IndicatorRecord indicatorRecord = new IndicatorRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.399+02:00", comments="Source field: indicator.id") public static final SqlColumn id = indicatorRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.399+02:00", comments="Source field: indicator.exam_id") public static final SqlColumn examId = indicatorRecord.examId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source field: indicator.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.399+02:00", comments="Source field: indicator.type") public static final SqlColumn type = indicatorRecord.type; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source field: indicator.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.400+02:00", comments="Source field: indicator.name") public static final SqlColumn name = indicatorRecord.name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source field: indicator.color") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.400+02:00", comments="Source field: indicator.color") public static final SqlColumn color = indicatorRecord.color; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source field: indicator.icon") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.401+02:00", comments="Source field: indicator.icon") public static final SqlColumn icon = indicatorRecord.icon; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source field: indicator.tags") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.402+02:00", comments="Source field: indicator.tags") public static final SqlColumn tags = indicatorRecord.tags; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.399+02:00", comments="Source Table: indicator") public static final class IndicatorRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/IndicatorRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/IndicatorRecordMapper.java index db679708..f15464cb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/IndicatorRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/IndicatorRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface IndicatorRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.402+02:00", comments="Source Table: indicator") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.402+02:00", comments="Source Table: indicator") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.402+02:00", comments="Source Table: indicator") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.402+02:00", comments="Source Table: indicator") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -58,7 +58,7 @@ public interface IndicatorRecordMapper { }) IndicatorRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.402+02:00", comments="Source Table: indicator") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -71,22 +71,22 @@ public interface IndicatorRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.402+02:00", comments="Source Table: indicator") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.402+02:00", comments="Source Table: indicator") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(indicatorRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.402+02:00", comments="Source Table: indicator") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, indicatorRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.402+02:00", comments="Source Table: indicator") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, indicatorRecord) .where(id, isEqualTo(id_)) @@ -94,7 +94,7 @@ public interface IndicatorRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.402+02:00", comments="Source Table: indicator") default int insert(IndicatorRecord record) { return insert(SqlBuilder.insert(record) .into(indicatorRecord) @@ -108,7 +108,7 @@ public interface IndicatorRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.402+02:00", comments="Source Table: indicator") default int insertSelective(IndicatorRecord record) { return insert(SqlBuilder.insert(record) .into(indicatorRecord) @@ -122,19 +122,19 @@ public interface IndicatorRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.168+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.402+02:00", comments="Source Table: indicator") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, examId, type, name, color, icon, tags) .from(indicatorRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.169+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.403+02:00", comments="Source Table: indicator") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, examId, type, name, color, icon, tags) .from(indicatorRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.169+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.403+02:00", comments="Source Table: indicator") default IndicatorRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, examId, type, name, color, icon, tags) .from(indicatorRecord) @@ -143,7 +143,7 @@ public interface IndicatorRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.169+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.403+02:00", comments="Source Table: indicator") default UpdateDSL> updateByExample(IndicatorRecord record) { return UpdateDSL.updateWithMapper(this::update, indicatorRecord) .set(examId).equalTo(record::getExamId) @@ -154,7 +154,7 @@ public interface IndicatorRecordMapper { .set(tags).equalTo(record::getTags); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.169+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.403+02:00", comments="Source Table: indicator") default UpdateDSL> updateByExampleSelective(IndicatorRecord record) { return UpdateDSL.updateWithMapper(this::update, indicatorRecord) .set(examId).equalToWhenPresent(record::getExamId) @@ -165,7 +165,7 @@ public interface IndicatorRecordMapper { .set(tags).equalToWhenPresent(record::getTags); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.169+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.403+02:00", comments="Source Table: indicator") default int updateByPrimaryKey(IndicatorRecord record) { return UpdateDSL.updateWithMapper(this::update, indicatorRecord) .set(examId).equalTo(record::getExamId) @@ -179,7 +179,7 @@ public interface IndicatorRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.169+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.403+02:00", comments="Source Table: indicator") default int updateByPrimaryKeySelective(IndicatorRecord record) { return UpdateDSL.updateWithMapper(this::update, indicatorRecord) .set(examId).equalToWhenPresent(record::getExamId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/InstitutionRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/InstitutionRecordDynamicSqlSupport.java index 90ece68f..71ead775 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/InstitutionRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/InstitutionRecordDynamicSqlSupport.java @@ -6,28 +6,28 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class InstitutionRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.175+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.413+02:00", comments="Source Table: institution") public static final InstitutionRecord institutionRecord = new InstitutionRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.175+01:00", comments="Source field: institution.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.413+02:00", comments="Source field: institution.id") public static final SqlColumn id = institutionRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.175+01:00", comments="Source field: institution.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.413+02:00", comments="Source field: institution.name") public static final SqlColumn name = institutionRecord.name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.175+01:00", comments="Source field: institution.url_suffix") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.413+02:00", comments="Source field: institution.url_suffix") public static final SqlColumn urlSuffix = institutionRecord.urlSuffix; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.175+01:00", comments="Source field: institution.theme_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.413+02:00", comments="Source field: institution.theme_name") public static final SqlColumn themeName = institutionRecord.themeName; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.175+01:00", comments="Source field: institution.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source field: institution.active") public static final SqlColumn active = institutionRecord.active; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.175+01:00", comments="Source field: institution.logo_image") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source field: institution.logo_image") public static final SqlColumn logoImage = institutionRecord.logoImage; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.175+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.413+02:00", comments="Source Table: institution") public static final class InstitutionRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/InstitutionRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/InstitutionRecordMapper.java index b2334f08..41cc8e4d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/InstitutionRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/InstitutionRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface InstitutionRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -57,7 +57,7 @@ public interface InstitutionRecordMapper { }) InstitutionRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -69,22 +69,22 @@ public interface InstitutionRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(institutionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, institutionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, institutionRecord) .where(id, isEqualTo(id_)) @@ -92,7 +92,7 @@ public interface InstitutionRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") default int insert(InstitutionRecord record) { return insert(SqlBuilder.insert(record) .into(institutionRecord) @@ -105,7 +105,7 @@ public interface InstitutionRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") default int insertSelective(InstitutionRecord record) { return insert(SqlBuilder.insert(record) .into(institutionRecord) @@ -118,19 +118,19 @@ public interface InstitutionRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, name, urlSuffix, themeName, active, logoImage) .from(institutionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, name, urlSuffix, themeName, active, logoImage) .from(institutionRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") default InstitutionRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, name, urlSuffix, themeName, active, logoImage) .from(institutionRecord) @@ -139,7 +139,7 @@ public interface InstitutionRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") default UpdateDSL> updateByExample(InstitutionRecord record) { return UpdateDSL.updateWithMapper(this::update, institutionRecord) .set(name).equalTo(record::getName) @@ -149,7 +149,7 @@ public interface InstitutionRecordMapper { .set(logoImage).equalTo(record::getLogoImage); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") default UpdateDSL> updateByExampleSelective(InstitutionRecord record) { return UpdateDSL.updateWithMapper(this::update, institutionRecord) .set(name).equalToWhenPresent(record::getName) @@ -159,7 +159,7 @@ public interface InstitutionRecordMapper { .set(logoImage).equalToWhenPresent(record::getLogoImage); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.414+02:00", comments="Source Table: institution") default int updateByPrimaryKey(InstitutionRecord record) { return UpdateDSL.updateWithMapper(this::update, institutionRecord) .set(name).equalTo(record::getName) @@ -172,7 +172,7 @@ public interface InstitutionRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.176+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.415+02:00", comments="Source Table: institution") default int updateByPrimaryKeySelective(InstitutionRecord record) { return UpdateDSL.updateWithMapper(this::update, institutionRecord) .set(name).equalToWhenPresent(record::getName) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/LmsSetupRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/LmsSetupRecordDynamicSqlSupport.java index 152bb1c1..5fa04cab 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/LmsSetupRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/LmsSetupRecordDynamicSqlSupport.java @@ -6,52 +6,52 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class LmsSetupRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.422+02:00", comments="Source Table: lms_setup") public static final LmsSetupRecord lmsSetupRecord = new LmsSetupRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.422+02:00", comments="Source field: lms_setup.id") public static final SqlColumn id = lmsSetupRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.183+01:00", comments="Source field: lms_setup.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.422+02:00", comments="Source field: lms_setup.institution_id") public static final SqlColumn institutionId = lmsSetupRecord.institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.183+01:00", comments="Source field: lms_setup.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.422+02:00", comments="Source field: lms_setup.name") public static final SqlColumn name = lmsSetupRecord.name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.183+01:00", comments="Source field: lms_setup.lms_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.422+02:00", comments="Source field: lms_setup.lms_type") public static final SqlColumn lmsType = lmsSetupRecord.lmsType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.183+01:00", comments="Source field: lms_setup.lms_url") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.422+02:00", comments="Source field: lms_setup.lms_url") public static final SqlColumn lmsUrl = lmsSetupRecord.lmsUrl; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.183+01:00", comments="Source field: lms_setup.lms_clientname") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.422+02:00", comments="Source field: lms_setup.lms_clientname") public static final SqlColumn lmsClientname = lmsSetupRecord.lmsClientname; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.183+01:00", comments="Source field: lms_setup.lms_clientsecret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.422+02:00", comments="Source field: lms_setup.lms_clientsecret") public static final SqlColumn lmsClientsecret = lmsSetupRecord.lmsClientsecret; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.184+01:00", comments="Source field: lms_setup.lms_rest_api_token") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.422+02:00", comments="Source field: lms_setup.lms_rest_api_token") public static final SqlColumn lmsRestApiToken = lmsSetupRecord.lmsRestApiToken; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source field: lms_setup.lms_proxy_host") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.422+02:00", comments="Source field: lms_setup.lms_proxy_host") public static final SqlColumn lmsProxyHost = lmsSetupRecord.lmsProxyHost; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source field: lms_setup.lms_proxy_port") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source field: lms_setup.lms_proxy_port") public static final SqlColumn lmsProxyPort = lmsSetupRecord.lmsProxyPort; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source field: lms_setup.lms_proxy_auth_username") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source field: lms_setup.lms_proxy_auth_username") public static final SqlColumn lmsProxyAuthUsername = lmsSetupRecord.lmsProxyAuthUsername; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source field: lms_setup.lms_proxy_auth_secret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source field: lms_setup.lms_proxy_auth_secret") public static final SqlColumn lmsProxyAuthSecret = lmsSetupRecord.lmsProxyAuthSecret; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source field: lms_setup.update_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source field: lms_setup.update_time") public static final SqlColumn updateTime = lmsSetupRecord.updateTime; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source field: lms_setup.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source field: lms_setup.active") public static final SqlColumn active = lmsSetupRecord.active; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.422+02:00", comments="Source Table: lms_setup") public static final class LmsSetupRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/LmsSetupRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/LmsSetupRecordMapper.java index f25c26ff..da1f8659 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/LmsSetupRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/LmsSetupRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface LmsSetupRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source Table: lms_setup") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source Table: lms_setup") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source Table: lms_setup") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source Table: lms_setup") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -65,7 +65,7 @@ public interface LmsSetupRecordMapper { }) LmsSetupRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source Table: lms_setup") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -85,22 +85,22 @@ public interface LmsSetupRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source Table: lms_setup") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source Table: lms_setup") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(lmsSetupRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source Table: lms_setup") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, lmsSetupRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source Table: lms_setup") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, lmsSetupRecord) .where(id, isEqualTo(id_)) @@ -108,7 +108,7 @@ public interface LmsSetupRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.185+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source Table: lms_setup") default int insert(LmsSetupRecord record) { return insert(SqlBuilder.insert(record) .into(lmsSetupRecord) @@ -129,7 +129,7 @@ public interface LmsSetupRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.186+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.423+02:00", comments="Source Table: lms_setup") default int insertSelective(LmsSetupRecord record) { return insert(SqlBuilder.insert(record) .into(lmsSetupRecord) @@ -150,19 +150,19 @@ public interface LmsSetupRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.186+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.424+02:00", comments="Source Table: lms_setup") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, name, lmsType, lmsUrl, lmsClientname, lmsClientsecret, lmsRestApiToken, lmsProxyHost, lmsProxyPort, lmsProxyAuthUsername, lmsProxyAuthSecret, updateTime, active) .from(lmsSetupRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.186+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.424+02:00", comments="Source Table: lms_setup") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, name, lmsType, lmsUrl, lmsClientname, lmsClientsecret, lmsRestApiToken, lmsProxyHost, lmsProxyPort, lmsProxyAuthUsername, lmsProxyAuthSecret, updateTime, active) .from(lmsSetupRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.186+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.424+02:00", comments="Source Table: lms_setup") default LmsSetupRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, name, lmsType, lmsUrl, lmsClientname, lmsClientsecret, lmsRestApiToken, lmsProxyHost, lmsProxyPort, lmsProxyAuthUsername, lmsProxyAuthSecret, updateTime, active) .from(lmsSetupRecord) @@ -171,7 +171,7 @@ public interface LmsSetupRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.186+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.424+02:00", comments="Source Table: lms_setup") default UpdateDSL> updateByExample(LmsSetupRecord record) { return UpdateDSL.updateWithMapper(this::update, lmsSetupRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -189,7 +189,7 @@ public interface LmsSetupRecordMapper { .set(active).equalTo(record::getActive); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.186+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.424+02:00", comments="Source Table: lms_setup") default UpdateDSL> updateByExampleSelective(LmsSetupRecord record) { return UpdateDSL.updateWithMapper(this::update, lmsSetupRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) @@ -207,7 +207,7 @@ public interface LmsSetupRecordMapper { .set(active).equalToWhenPresent(record::getActive); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.186+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.424+02:00", comments="Source Table: lms_setup") default int updateByPrimaryKey(LmsSetupRecord record) { return UpdateDSL.updateWithMapper(this::update, lmsSetupRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -228,7 +228,7 @@ public interface LmsSetupRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.186+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.424+02:00", comments="Source Table: lms_setup") default int updateByPrimaryKeySelective(LmsSetupRecord record) { return UpdateDSL.updateWithMapper(this::update, lmsSetupRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/OrientationRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/OrientationRecordDynamicSqlSupport.java index 2575a605..404040de 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/OrientationRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/OrientationRecordDynamicSqlSupport.java @@ -6,40 +6,40 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class OrientationRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.105+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.332+02:00", comments="Source Table: orientation") public static final OrientationRecord orientationRecord = new OrientationRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.105+01:00", comments="Source field: orientation.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.332+02:00", comments="Source field: orientation.id") public static final SqlColumn id = orientationRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.105+01:00", comments="Source field: orientation.config_attribute_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.333+02:00", comments="Source field: orientation.config_attribute_id") public static final SqlColumn configAttributeId = orientationRecord.configAttributeId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.105+01:00", comments="Source field: orientation.template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.333+02:00", comments="Source field: orientation.template_id") public static final SqlColumn templateId = orientationRecord.templateId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.106+01:00", comments="Source field: orientation.view_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.333+02:00", comments="Source field: orientation.view_id") public static final SqlColumn viewId = orientationRecord.viewId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.106+01:00", comments="Source field: orientation.group_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.333+02:00", comments="Source field: orientation.group_id") public static final SqlColumn groupId = orientationRecord.groupId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.106+01:00", comments="Source field: orientation.x_position") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.333+02:00", comments="Source field: orientation.x_position") public static final SqlColumn xPosition = orientationRecord.xPosition; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.106+01:00", comments="Source field: orientation.y_position") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.333+02:00", comments="Source field: orientation.y_position") public static final SqlColumn yPosition = orientationRecord.yPosition; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.107+01:00", comments="Source field: orientation.width") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.334+02:00", comments="Source field: orientation.width") public static final SqlColumn width = orientationRecord.width; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.107+01:00", comments="Source field: orientation.height") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.334+02:00", comments="Source field: orientation.height") public static final SqlColumn height = orientationRecord.height; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.107+01:00", comments="Source field: orientation.title") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.334+02:00", comments="Source field: orientation.title") public static final SqlColumn title = orientationRecord.title; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.105+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.332+02:00", comments="Source Table: orientation") public static final class OrientationRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/OrientationRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/OrientationRecordMapper.java index e4906149..cc29b865 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/OrientationRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/OrientationRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface OrientationRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.107+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.334+02:00", comments="Source Table: orientation") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.107+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.334+02:00", comments="Source Table: orientation") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.107+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.334+02:00", comments="Source Table: orientation") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.107+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.334+02:00", comments="Source Table: orientation") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -61,7 +61,7 @@ public interface OrientationRecordMapper { }) OrientationRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.107+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.334+02:00", comments="Source Table: orientation") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -77,22 +77,22 @@ public interface OrientationRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.107+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.335+02:00", comments="Source Table: orientation") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.107+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.335+02:00", comments="Source Table: orientation") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(orientationRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.107+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.335+02:00", comments="Source Table: orientation") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, orientationRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.107+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.335+02:00", comments="Source Table: orientation") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, orientationRecord) .where(id, isEqualTo(id_)) @@ -100,7 +100,7 @@ public interface OrientationRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.108+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.335+02:00", comments="Source Table: orientation") default int insert(OrientationRecord record) { return insert(SqlBuilder.insert(record) .into(orientationRecord) @@ -117,7 +117,7 @@ public interface OrientationRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.108+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.335+02:00", comments="Source Table: orientation") default int insertSelective(OrientationRecord record) { return insert(SqlBuilder.insert(record) .into(orientationRecord) @@ -134,19 +134,19 @@ public interface OrientationRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.108+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.335+02:00", comments="Source Table: orientation") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, configAttributeId, templateId, viewId, groupId, xPosition, yPosition, width, height, title) .from(orientationRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.108+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.335+02:00", comments="Source Table: orientation") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, configAttributeId, templateId, viewId, groupId, xPosition, yPosition, width, height, title) .from(orientationRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.108+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.336+02:00", comments="Source Table: orientation") default OrientationRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, configAttributeId, templateId, viewId, groupId, xPosition, yPosition, width, height, title) .from(orientationRecord) @@ -155,7 +155,7 @@ public interface OrientationRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.108+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.336+02:00", comments="Source Table: orientation") default UpdateDSL> updateByExample(OrientationRecord record) { return UpdateDSL.updateWithMapper(this::update, orientationRecord) .set(configAttributeId).equalTo(record::getConfigAttributeId) @@ -169,7 +169,7 @@ public interface OrientationRecordMapper { .set(title).equalTo(record::getTitle); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.108+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.336+02:00", comments="Source Table: orientation") default UpdateDSL> updateByExampleSelective(OrientationRecord record) { return UpdateDSL.updateWithMapper(this::update, orientationRecord) .set(configAttributeId).equalToWhenPresent(record::getConfigAttributeId) @@ -183,7 +183,7 @@ public interface OrientationRecordMapper { .set(title).equalToWhenPresent(record::getTitle); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.108+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.336+02:00", comments="Source Table: orientation") default int updateByPrimaryKey(OrientationRecord record) { return UpdateDSL.updateWithMapper(this::update, orientationRecord) .set(configAttributeId).equalTo(record::getConfigAttributeId) @@ -200,7 +200,7 @@ public interface OrientationRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.108+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.336+02:00", comments="Source Table: orientation") default int updateByPrimaryKeySelective(OrientationRecord record) { return UpdateDSL.updateWithMapper(this::update, orientationRecord) .set(configAttributeId).equalToWhenPresent(record::getConfigAttributeId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RemoteProctoringRoomRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RemoteProctoringRoomRecordDynamicSqlSupport.java index ff5539a0..fec8d832 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RemoteProctoringRoomRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RemoteProctoringRoomRecordDynamicSqlSupport.java @@ -6,37 +6,37 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class RemoteProctoringRoomRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.145+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.380+02:00", comments="Source Table: remote_proctoring_room") public static final RemoteProctoringRoomRecord remoteProctoringRoomRecord = new RemoteProctoringRoomRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.145+01:00", comments="Source field: remote_proctoring_room.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.381+02:00", comments="Source field: remote_proctoring_room.id") public static final SqlColumn id = remoteProctoringRoomRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.145+01:00", comments="Source field: remote_proctoring_room.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.381+02:00", comments="Source field: remote_proctoring_room.exam_id") public static final SqlColumn examId = remoteProctoringRoomRecord.examId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.145+01:00", comments="Source field: remote_proctoring_room.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.381+02:00", comments="Source field: remote_proctoring_room.name") public static final SqlColumn name = remoteProctoringRoomRecord.name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.145+01:00", comments="Source field: remote_proctoring_room.size") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.381+02:00", comments="Source field: remote_proctoring_room.size") public static final SqlColumn size = remoteProctoringRoomRecord.size; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.146+01:00", comments="Source field: remote_proctoring_room.subject") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.381+02:00", comments="Source field: remote_proctoring_room.subject") public static final SqlColumn subject = remoteProctoringRoomRecord.subject; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.146+01:00", comments="Source field: remote_proctoring_room.townhall_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.381+02:00", comments="Source field: remote_proctoring_room.townhall_room") public static final SqlColumn townhallRoom = remoteProctoringRoomRecord.townhallRoom; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.147+01:00", comments="Source field: remote_proctoring_room.break_out_connections") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.381+02:00", comments="Source field: remote_proctoring_room.break_out_connections") public static final SqlColumn breakOutConnections = remoteProctoringRoomRecord.breakOutConnections; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.148+01:00", comments="Source field: remote_proctoring_room.join_key") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.382+02:00", comments="Source field: remote_proctoring_room.join_key") public static final SqlColumn joinKey = remoteProctoringRoomRecord.joinKey; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.148+01:00", comments="Source field: remote_proctoring_room.room_data") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.382+02:00", comments="Source field: remote_proctoring_room.room_data") public static final SqlColumn roomData = remoteProctoringRoomRecord.roomData; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.145+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.381+02:00", comments="Source Table: remote_proctoring_room") public static final class RemoteProctoringRoomRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RemoteProctoringRoomRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RemoteProctoringRoomRecordMapper.java index d4613134..afd84ba8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RemoteProctoringRoomRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RemoteProctoringRoomRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface RemoteProctoringRoomRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.148+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.382+02:00", comments="Source Table: remote_proctoring_room") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.148+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.382+02:00", comments="Source Table: remote_proctoring_room") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.148+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.382+02:00", comments="Source Table: remote_proctoring_room") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.148+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.383+02:00", comments="Source Table: remote_proctoring_room") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -60,7 +60,7 @@ public interface RemoteProctoringRoomRecordMapper { }) RemoteProctoringRoomRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.148+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.383+02:00", comments="Source Table: remote_proctoring_room") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -75,22 +75,22 @@ public interface RemoteProctoringRoomRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.149+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.383+02:00", comments="Source Table: remote_proctoring_room") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.149+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.384+02:00", comments="Source Table: remote_proctoring_room") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(remoteProctoringRoomRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.149+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.384+02:00", comments="Source Table: remote_proctoring_room") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, remoteProctoringRoomRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.149+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.384+02:00", comments="Source Table: remote_proctoring_room") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, remoteProctoringRoomRecord) .where(id, isEqualTo(id_)) @@ -98,7 +98,7 @@ public interface RemoteProctoringRoomRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.149+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.384+02:00", comments="Source Table: remote_proctoring_room") default int insert(RemoteProctoringRoomRecord record) { return insert(SqlBuilder.insert(record) .into(remoteProctoringRoomRecord) @@ -114,7 +114,7 @@ public interface RemoteProctoringRoomRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.149+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.384+02:00", comments="Source Table: remote_proctoring_room") default int insertSelective(RemoteProctoringRoomRecord record) { return insert(SqlBuilder.insert(record) .into(remoteProctoringRoomRecord) @@ -130,19 +130,19 @@ public interface RemoteProctoringRoomRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.149+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.384+02:00", comments="Source Table: remote_proctoring_room") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, examId, name, size, subject, townhallRoom, breakOutConnections, joinKey, roomData) .from(remoteProctoringRoomRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.149+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.384+02:00", comments="Source Table: remote_proctoring_room") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, examId, name, size, subject, townhallRoom, breakOutConnections, joinKey, roomData) .from(remoteProctoringRoomRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.149+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.384+02:00", comments="Source Table: remote_proctoring_room") default RemoteProctoringRoomRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, examId, name, size, subject, townhallRoom, breakOutConnections, joinKey, roomData) .from(remoteProctoringRoomRecord) @@ -151,7 +151,7 @@ public interface RemoteProctoringRoomRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.149+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.384+02:00", comments="Source Table: remote_proctoring_room") default UpdateDSL> updateByExample(RemoteProctoringRoomRecord record) { return UpdateDSL.updateWithMapper(this::update, remoteProctoringRoomRecord) .set(examId).equalTo(record::getExamId) @@ -164,7 +164,7 @@ public interface RemoteProctoringRoomRecordMapper { .set(roomData).equalTo(record::getRoomData); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.149+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.384+02:00", comments="Source Table: remote_proctoring_room") default UpdateDSL> updateByExampleSelective(RemoteProctoringRoomRecord record) { return UpdateDSL.updateWithMapper(this::update, remoteProctoringRoomRecord) .set(examId).equalToWhenPresent(record::getExamId) @@ -177,7 +177,7 @@ public interface RemoteProctoringRoomRecordMapper { .set(roomData).equalToWhenPresent(record::getRoomData); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.149+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.384+02:00", comments="Source Table: remote_proctoring_room") default int updateByPrimaryKey(RemoteProctoringRoomRecord record) { return UpdateDSL.updateWithMapper(this::update, remoteProctoringRoomRecord) .set(examId).equalTo(record::getExamId) @@ -193,7 +193,7 @@ public interface RemoteProctoringRoomRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.149+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.384+02:00", comments="Source Table: remote_proctoring_room") default int updateByPrimaryKeySelective(RemoteProctoringRoomRecord record) { return UpdateDSL.updateWithMapper(this::update, remoteProctoringRoomRecord) .set(examId).equalToWhenPresent(record::getExamId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RoleRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RoleRecordDynamicSqlSupport.java index e19c58d9..794a683f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RoleRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RoleRecordDynamicSqlSupport.java @@ -6,19 +6,19 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class RoleRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.197+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.429+02:00", comments="Source Table: user_role") public static final RoleRecord roleRecord = new RoleRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.197+01:00", comments="Source field: user_role.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source field: user_role.id") public static final SqlColumn id = roleRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.198+01:00", comments="Source field: user_role.user_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source field: user_role.user_id") public static final SqlColumn userId = roleRecord.userId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.198+01:00", comments="Source field: user_role.role_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source field: user_role.role_name") public static final SqlColumn roleName = roleRecord.roleName; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.197+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source Table: user_role") public static final class RoleRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RoleRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RoleRecordMapper.java index 315dcf2d..ddc3a1ab 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RoleRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/RoleRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface RoleRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.198+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source Table: user_role") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.198+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source Table: user_role") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.198+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source Table: user_role") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.198+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source Table: user_role") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -54,7 +54,7 @@ public interface RoleRecordMapper { }) RoleRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.198+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source Table: user_role") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -63,22 +63,22 @@ public interface RoleRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.198+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source Table: user_role") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source Table: user_role") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(roleRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source Table: user_role") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, roleRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source Table: user_role") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, roleRecord) .where(id, isEqualTo(id_)) @@ -86,7 +86,7 @@ public interface RoleRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source Table: user_role") default int insert(RoleRecord record) { return insert(SqlBuilder.insert(record) .into(roleRecord) @@ -96,7 +96,7 @@ public interface RoleRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source Table: user_role") default int insertSelective(RoleRecord record) { return insert(SqlBuilder.insert(record) .into(roleRecord) @@ -106,19 +106,19 @@ public interface RoleRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source Table: user_role") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, userId, roleName) .from(roleRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.430+02:00", comments="Source Table: user_role") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, userId, roleName) .from(roleRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.431+02:00", comments="Source Table: user_role") default RoleRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, userId, roleName) .from(roleRecord) @@ -127,21 +127,21 @@ public interface RoleRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.431+02:00", comments="Source Table: user_role") default UpdateDSL> updateByExample(RoleRecord record) { return UpdateDSL.updateWithMapper(this::update, roleRecord) .set(userId).equalTo(record::getUserId) .set(roleName).equalTo(record::getRoleName); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.431+02:00", comments="Source Table: user_role") default UpdateDSL> updateByExampleSelective(RoleRecord record) { return UpdateDSL.updateWithMapper(this::update, roleRecord) .set(userId).equalToWhenPresent(record::getUserId) .set(roleName).equalToWhenPresent(record::getRoleName); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.431+02:00", comments="Source Table: user_role") default int updateByPrimaryKey(RoleRecord record) { return UpdateDSL.updateWithMapper(this::update, roleRecord) .set(userId).equalTo(record::getUserId) @@ -151,7 +151,7 @@ public interface RoleRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.431+02:00", comments="Source Table: user_role") default int updateByPrimaryKeySelective(RoleRecord record) { return UpdateDSL.updateWithMapper(this::update, roleRecord) .set(userId).equalToWhenPresent(record::getUserId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/SebClientConfigRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/SebClientConfigRecordDynamicSqlSupport.java index 15ececde..7a8516ca 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/SebClientConfigRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/SebClientConfigRecordDynamicSqlSupport.java @@ -7,34 +7,40 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class SebClientConfigRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source Table: seb_client_configuration") public static final SebClientConfigRecord sebClientConfigRecord = new SebClientConfigRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source field: seb_client_configuration.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source field: seb_client_configuration.id") public static final SqlColumn id = sebClientConfigRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source field: seb_client_configuration.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.419+02:00", comments="Source field: seb_client_configuration.institution_id") public static final SqlColumn institutionId = sebClientConfigRecord.institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source field: seb_client_configuration.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.419+02:00", comments="Source field: seb_client_configuration.name") public static final SqlColumn name = sebClientConfigRecord.name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source field: seb_client_configuration.date") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.419+02:00", comments="Source field: seb_client_configuration.date") public static final SqlColumn date = sebClientConfigRecord.date; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source field: seb_client_configuration.client_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.419+02:00", comments="Source field: seb_client_configuration.client_name") public static final SqlColumn clientName = sebClientConfigRecord.clientName; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source field: seb_client_configuration.client_secret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.419+02:00", comments="Source field: seb_client_configuration.client_secret") public static final SqlColumn clientSecret = sebClientConfigRecord.clientSecret; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source field: seb_client_configuration.encrypt_secret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.419+02:00", comments="Source field: seb_client_configuration.encrypt_secret") public static final SqlColumn encryptSecret = sebClientConfigRecord.encryptSecret; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source field: seb_client_configuration.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.419+02:00", comments="Source field: seb_client_configuration.active") public static final SqlColumn active = sebClientConfigRecord.active; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.419+02:00", comments="Source field: seb_client_configuration.last_update_time") + public static final SqlColumn lastUpdateTime = sebClientConfigRecord.lastUpdateTime; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.419+02:00", comments="Source field: seb_client_configuration.last_update_user") + public static final SqlColumn lastUpdateUser = sebClientConfigRecord.lastUpdateUser; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source Table: seb_client_configuration") public static final class SebClientConfigRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); @@ -52,6 +58,10 @@ public final class SebClientConfigRecordDynamicSqlSupport { public final SqlColumn active = column("active", JDBCType.INTEGER); + public final SqlColumn lastUpdateTime = column("last_update_time", JDBCType.BIGINT); + + public final SqlColumn lastUpdateUser = column("last_update_user", JDBCType.VARCHAR); + public SebClientConfigRecord() { super("seb_client_configuration"); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/SebClientConfigRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/SebClientConfigRecordMapper.java index 46e35138..89a2e87e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/SebClientConfigRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/SebClientConfigRecordMapper.java @@ -34,20 +34,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface SebClientConfigRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.419+02:00", comments="Source Table: seb_client_configuration") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.419+02:00", comments="Source Table: seb_client_configuration") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.419+02:00", comments="Source Table: seb_client_configuration") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.419+02:00", comments="Source Table: seb_client_configuration") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -57,11 +57,13 @@ public interface SebClientConfigRecordMapper { @Arg(column="client_name", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="client_secret", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="encrypt_secret", javaType=String.class, jdbcType=JdbcType.VARCHAR), - @Arg(column="active", javaType=Integer.class, jdbcType=JdbcType.INTEGER) + @Arg(column="active", javaType=Integer.class, jdbcType=JdbcType.INTEGER), + @Arg(column="last_update_time", javaType=Long.class, jdbcType=JdbcType.BIGINT), + @Arg(column="last_update_user", javaType=String.class, jdbcType=JdbcType.VARCHAR) }) SebClientConfigRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.420+02:00", comments="Source Table: seb_client_configuration") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -71,26 +73,28 @@ public interface SebClientConfigRecordMapper { @Arg(column="client_name", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="client_secret", javaType=String.class, jdbcType=JdbcType.VARCHAR), @Arg(column="encrypt_secret", javaType=String.class, jdbcType=JdbcType.VARCHAR), - @Arg(column="active", javaType=Integer.class, jdbcType=JdbcType.INTEGER) + @Arg(column="active", javaType=Integer.class, jdbcType=JdbcType.INTEGER), + @Arg(column="last_update_time", javaType=Long.class, jdbcType=JdbcType.BIGINT), + @Arg(column="last_update_user", javaType=String.class, jdbcType=JdbcType.VARCHAR) }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.420+02:00", comments="Source Table: seb_client_configuration") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.420+02:00", comments="Source Table: seb_client_configuration") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(sebClientConfigRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.420+02:00", comments="Source Table: seb_client_configuration") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, sebClientConfigRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.420+02:00", comments="Source Table: seb_client_configuration") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, sebClientConfigRecord) .where(id, isEqualTo(id_)) @@ -98,7 +102,7 @@ public interface SebClientConfigRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.420+02:00", comments="Source Table: seb_client_configuration") default int insert(SebClientConfigRecord record) { return insert(SqlBuilder.insert(record) .into(sebClientConfigRecord) @@ -109,11 +113,13 @@ public interface SebClientConfigRecordMapper { .map(clientSecret).toProperty("clientSecret") .map(encryptSecret).toProperty("encryptSecret") .map(active).toProperty("active") + .map(lastUpdateTime).toProperty("lastUpdateTime") + .map(lastUpdateUser).toProperty("lastUpdateUser") .build() .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.420+02:00", comments="Source Table: seb_client_configuration") default int insertSelective(SebClientConfigRecord record) { return insert(SqlBuilder.insert(record) .into(sebClientConfigRecord) @@ -124,32 +130,34 @@ public interface SebClientConfigRecordMapper { .map(clientSecret).toPropertyWhenPresent("clientSecret", record::getClientSecret) .map(encryptSecret).toPropertyWhenPresent("encryptSecret", record::getEncryptSecret) .map(active).toPropertyWhenPresent("active", record::getActive) + .map(lastUpdateTime).toPropertyWhenPresent("lastUpdateTime", record::getLastUpdateTime) + .map(lastUpdateUser).toPropertyWhenPresent("lastUpdateUser", record::getLastUpdateUser) .build() .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.420+02:00", comments="Source Table: seb_client_configuration") default QueryExpressionDSL>> selectByExample() { - return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, name, date, clientName, clientSecret, encryptSecret, active) + return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, name, date, clientName, clientSecret, encryptSecret, active, lastUpdateTime, lastUpdateUser) .from(sebClientConfigRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.420+02:00", comments="Source Table: seb_client_configuration") default QueryExpressionDSL>> selectDistinctByExample() { - return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, name, date, clientName, clientSecret, encryptSecret, active) + return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, name, date, clientName, clientSecret, encryptSecret, active, lastUpdateTime, lastUpdateUser) .from(sebClientConfigRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.178+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.420+02:00", comments="Source Table: seb_client_configuration") default SebClientConfigRecord selectByPrimaryKey(Long id_) { - return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, name, date, clientName, clientSecret, encryptSecret, active) + return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, name, date, clientName, clientSecret, encryptSecret, active, lastUpdateTime, lastUpdateUser) .from(sebClientConfigRecord) .where(id, isEqualTo(id_)) .build() .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.180+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.420+02:00", comments="Source Table: seb_client_configuration") default UpdateDSL> updateByExample(SebClientConfigRecord record) { return UpdateDSL.updateWithMapper(this::update, sebClientConfigRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -158,10 +166,12 @@ public interface SebClientConfigRecordMapper { .set(clientName).equalTo(record::getClientName) .set(clientSecret).equalTo(record::getClientSecret) .set(encryptSecret).equalTo(record::getEncryptSecret) - .set(active).equalTo(record::getActive); + .set(active).equalTo(record::getActive) + .set(lastUpdateTime).equalTo(record::getLastUpdateTime) + .set(lastUpdateUser).equalTo(record::getLastUpdateUser); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.180+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.420+02:00", comments="Source Table: seb_client_configuration") default UpdateDSL> updateByExampleSelective(SebClientConfigRecord record) { return UpdateDSL.updateWithMapper(this::update, sebClientConfigRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) @@ -170,10 +180,12 @@ public interface SebClientConfigRecordMapper { .set(clientName).equalToWhenPresent(record::getClientName) .set(clientSecret).equalToWhenPresent(record::getClientSecret) .set(encryptSecret).equalToWhenPresent(record::getEncryptSecret) - .set(active).equalToWhenPresent(record::getActive); + .set(active).equalToWhenPresent(record::getActive) + .set(lastUpdateTime).equalToWhenPresent(record::getLastUpdateTime) + .set(lastUpdateUser).equalToWhenPresent(record::getLastUpdateUser); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.180+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.420+02:00", comments="Source Table: seb_client_configuration") default int updateByPrimaryKey(SebClientConfigRecord record) { return UpdateDSL.updateWithMapper(this::update, sebClientConfigRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -183,12 +195,14 @@ public interface SebClientConfigRecordMapper { .set(clientSecret).equalTo(record::getClientSecret) .set(encryptSecret).equalTo(record::getEncryptSecret) .set(active).equalTo(record::getActive) + .set(lastUpdateTime).equalTo(record::getLastUpdateTime) + .set(lastUpdateUser).equalTo(record::getLastUpdateUser) .where(id, isEqualTo(record::getId)) .build() .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.180+01:00", comments="Source Table: seb_client_configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.420+02:00", comments="Source Table: seb_client_configuration") default int updateByPrimaryKeySelective(SebClientConfigRecord record) { return UpdateDSL.updateWithMapper(this::update, sebClientConfigRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) @@ -198,6 +212,8 @@ public interface SebClientConfigRecordMapper { .set(clientSecret).equalToWhenPresent(record::getClientSecret) .set(encryptSecret).equalToWhenPresent(record::getEncryptSecret) .set(active).equalToWhenPresent(record::getActive) + .set(lastUpdateTime).equalToWhenPresent(record::getLastUpdateTime) + .set(lastUpdateUser).equalToWhenPresent(record::getLastUpdateUser) .where(id, isEqualTo(record::getId)) .build() .execute(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ThresholdRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ThresholdRecordDynamicSqlSupport.java index c02a6e40..f1278958 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ThresholdRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ThresholdRecordDynamicSqlSupport.java @@ -7,25 +7,25 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class ThresholdRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.170+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.405+02:00", comments="Source Table: threshold") public static final ThresholdRecord thresholdRecord = new ThresholdRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.170+01:00", comments="Source field: threshold.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.405+02:00", comments="Source field: threshold.id") public static final SqlColumn id = thresholdRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.170+01:00", comments="Source field: threshold.indicator_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.405+02:00", comments="Source field: threshold.indicator_id") public static final SqlColumn indicatorId = thresholdRecord.indicatorId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.170+01:00", comments="Source field: threshold.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.405+02:00", comments="Source field: threshold.value") public static final SqlColumn value = thresholdRecord.value; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.171+01:00", comments="Source field: threshold.color") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.405+02:00", comments="Source field: threshold.color") public static final SqlColumn color = thresholdRecord.color; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.171+01:00", comments="Source field: threshold.icon") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.405+02:00", comments="Source field: threshold.icon") public static final SqlColumn icon = thresholdRecord.icon; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.170+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.405+02:00", comments="Source Table: threshold") public static final class ThresholdRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ThresholdRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ThresholdRecordMapper.java index 752c619c..e331ecff 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ThresholdRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ThresholdRecordMapper.java @@ -33,20 +33,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface ThresholdRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.171+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.405+02:00", comments="Source Table: threshold") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.171+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.405+02:00", comments="Source Table: threshold") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.172+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.405+02:00", comments="Source Table: threshold") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.172+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.405+02:00", comments="Source Table: threshold") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -57,7 +57,7 @@ public interface ThresholdRecordMapper { }) ThresholdRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.172+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.406+02:00", comments="Source Table: threshold") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -68,22 +68,22 @@ public interface ThresholdRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.172+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.406+02:00", comments="Source Table: threshold") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.172+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.406+02:00", comments="Source Table: threshold") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(thresholdRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.172+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.406+02:00", comments="Source Table: threshold") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, thresholdRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.172+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.406+02:00", comments="Source Table: threshold") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, thresholdRecord) .where(id, isEqualTo(id_)) @@ -91,7 +91,7 @@ public interface ThresholdRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.172+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.406+02:00", comments="Source Table: threshold") default int insert(ThresholdRecord record) { return insert(SqlBuilder.insert(record) .into(thresholdRecord) @@ -103,7 +103,7 @@ public interface ThresholdRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.172+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.406+02:00", comments="Source Table: threshold") default int insertSelective(ThresholdRecord record) { return insert(SqlBuilder.insert(record) .into(thresholdRecord) @@ -115,19 +115,19 @@ public interface ThresholdRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.173+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.406+02:00", comments="Source Table: threshold") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, indicatorId, value, color, icon) .from(thresholdRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.173+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.406+02:00", comments="Source Table: threshold") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, indicatorId, value, color, icon) .from(thresholdRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.173+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.406+02:00", comments="Source Table: threshold") default ThresholdRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, indicatorId, value, color, icon) .from(thresholdRecord) @@ -136,7 +136,7 @@ public interface ThresholdRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.173+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.406+02:00", comments="Source Table: threshold") default UpdateDSL> updateByExample(ThresholdRecord record) { return UpdateDSL.updateWithMapper(this::update, thresholdRecord) .set(indicatorId).equalTo(record::getIndicatorId) @@ -145,7 +145,7 @@ public interface ThresholdRecordMapper { .set(icon).equalTo(record::getIcon); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.173+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.406+02:00", comments="Source Table: threshold") default UpdateDSL> updateByExampleSelective(ThresholdRecord record) { return UpdateDSL.updateWithMapper(this::update, thresholdRecord) .set(indicatorId).equalToWhenPresent(record::getIndicatorId) @@ -154,7 +154,7 @@ public interface ThresholdRecordMapper { .set(icon).equalToWhenPresent(record::getIcon); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.173+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.406+02:00", comments="Source Table: threshold") default int updateByPrimaryKey(ThresholdRecord record) { return UpdateDSL.updateWithMapper(this::update, thresholdRecord) .set(indicatorId).equalTo(record::getIndicatorId) @@ -166,7 +166,7 @@ public interface ThresholdRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.173+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.410+02:00", comments="Source Table: threshold") default int updateByPrimaryKeySelective(ThresholdRecord record) { return UpdateDSL.updateWithMapper(this::update, thresholdRecord) .set(indicatorId).equalToWhenPresent(record::getIndicatorId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserActivityLogRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserActivityLogRecordDynamicSqlSupport.java index 41d761c1..9cb57f06 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserActivityLogRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserActivityLogRecordDynamicSqlSupport.java @@ -6,31 +6,31 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class UserActivityLogRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.200+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.437+02:00", comments="Source Table: user_activity_log") public static final UserActivityLogRecord userActivityLogRecord = new UserActivityLogRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.200+01:00", comments="Source field: user_activity_log.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.437+02:00", comments="Source field: user_activity_log.id") public static final SqlColumn id = userActivityLogRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.200+01:00", comments="Source field: user_activity_log.user_uuid") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.437+02:00", comments="Source field: user_activity_log.user_uuid") public static final SqlColumn userUuid = userActivityLogRecord.userUuid; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.201+01:00", comments="Source field: user_activity_log.timestamp") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.437+02:00", comments="Source field: user_activity_log.timestamp") public static final SqlColumn timestamp = userActivityLogRecord.timestamp; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.202+01:00", comments="Source field: user_activity_log.activity_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.437+02:00", comments="Source field: user_activity_log.activity_type") public static final SqlColumn activityType = userActivityLogRecord.activityType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.202+01:00", comments="Source field: user_activity_log.entity_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.437+02:00", comments="Source field: user_activity_log.entity_type") public static final SqlColumn entityType = userActivityLogRecord.entityType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.202+01:00", comments="Source field: user_activity_log.entity_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.437+02:00", comments="Source field: user_activity_log.entity_id") public static final SqlColumn entityId = userActivityLogRecord.entityId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.202+01:00", comments="Source field: user_activity_log.message") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.437+02:00", comments="Source field: user_activity_log.message") public static final SqlColumn message = userActivityLogRecord.message; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.200+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.437+02:00", comments="Source Table: user_activity_log") public static final class UserActivityLogRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserActivityLogRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserActivityLogRecordMapper.java index e1109174..d237ba6d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserActivityLogRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserActivityLogRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface UserActivityLogRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.202+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.437+02:00", comments="Source Table: user_activity_log") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.202+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.437+02:00", comments="Source Table: user_activity_log") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.202+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.437+02:00", comments="Source Table: user_activity_log") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.202+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.437+02:00", comments="Source Table: user_activity_log") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -58,7 +58,7 @@ public interface UserActivityLogRecordMapper { }) UserActivityLogRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.202+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.438+02:00", comments="Source Table: user_activity_log") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -71,22 +71,22 @@ public interface UserActivityLogRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.202+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.438+02:00", comments="Source Table: user_activity_log") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.202+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.438+02:00", comments="Source Table: user_activity_log") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(userActivityLogRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.202+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.438+02:00", comments="Source Table: user_activity_log") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, userActivityLogRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.203+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.438+02:00", comments="Source Table: user_activity_log") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, userActivityLogRecord) .where(id, isEqualTo(id_)) @@ -94,7 +94,7 @@ public interface UserActivityLogRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.203+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.438+02:00", comments="Source Table: user_activity_log") default int insert(UserActivityLogRecord record) { return insert(SqlBuilder.insert(record) .into(userActivityLogRecord) @@ -108,7 +108,7 @@ public interface UserActivityLogRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.203+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.438+02:00", comments="Source Table: user_activity_log") default int insertSelective(UserActivityLogRecord record) { return insert(SqlBuilder.insert(record) .into(userActivityLogRecord) @@ -122,19 +122,19 @@ public interface UserActivityLogRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.203+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.438+02:00", comments="Source Table: user_activity_log") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, userUuid, timestamp, activityType, entityType, entityId, message) .from(userActivityLogRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.203+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.438+02:00", comments="Source Table: user_activity_log") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, userUuid, timestamp, activityType, entityType, entityId, message) .from(userActivityLogRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.203+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.438+02:00", comments="Source Table: user_activity_log") default UserActivityLogRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, userUuid, timestamp, activityType, entityType, entityId, message) .from(userActivityLogRecord) @@ -143,7 +143,7 @@ public interface UserActivityLogRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.203+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.438+02:00", comments="Source Table: user_activity_log") default UpdateDSL> updateByExample(UserActivityLogRecord record) { return UpdateDSL.updateWithMapper(this::update, userActivityLogRecord) .set(userUuid).equalTo(record::getUserUuid) @@ -154,7 +154,7 @@ public interface UserActivityLogRecordMapper { .set(message).equalTo(record::getMessage); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.203+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.438+02:00", comments="Source Table: user_activity_log") default UpdateDSL> updateByExampleSelective(UserActivityLogRecord record) { return UpdateDSL.updateWithMapper(this::update, userActivityLogRecord) .set(userUuid).equalToWhenPresent(record::getUserUuid) @@ -165,7 +165,7 @@ public interface UserActivityLogRecordMapper { .set(message).equalToWhenPresent(record::getMessage); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.203+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.438+02:00", comments="Source Table: user_activity_log") default int updateByPrimaryKey(UserActivityLogRecord record) { return UpdateDSL.updateWithMapper(this::update, userActivityLogRecord) .set(userUuid).equalTo(record::getUserUuid) @@ -179,7 +179,7 @@ public interface UserActivityLogRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.203+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.438+02:00", comments="Source Table: user_activity_log") default int updateByPrimaryKeySelective(UserActivityLogRecord record) { return UpdateDSL.updateWithMapper(this::update, userActivityLogRecord) .set(userUuid).equalToWhenPresent(record::getUserUuid) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserRecordDynamicSqlSupport.java index 89d2a1d5..006361e5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserRecordDynamicSqlSupport.java @@ -7,46 +7,46 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class UserRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.426+02:00", comments="Source Table: user") public static final UserRecord userRecord = new UserRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source field: user.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.426+02:00", comments="Source field: user.id") public static final SqlColumn id = userRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source field: user.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.426+02:00", comments="Source field: user.institution_id") public static final SqlColumn institutionId = userRecord.institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source field: user.uuid") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.426+02:00", comments="Source field: user.uuid") public static final SqlColumn uuid = userRecord.uuid; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source field: user.creation_date") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.426+02:00", comments="Source field: user.creation_date") public static final SqlColumn creationDate = userRecord.creationDate; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source field: user.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.426+02:00", comments="Source field: user.name") public static final SqlColumn name = userRecord.name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source field: user.surname") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.426+02:00", comments="Source field: user.surname") public static final SqlColumn surname = userRecord.surname; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source field: user.username") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.426+02:00", comments="Source field: user.username") public static final SqlColumn username = userRecord.username; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source field: user.password") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.427+02:00", comments="Source field: user.password") public static final SqlColumn password = userRecord.password; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source field: user.email") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.427+02:00", comments="Source field: user.email") public static final SqlColumn email = userRecord.email; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source field: user.language") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.427+02:00", comments="Source field: user.language") public static final SqlColumn language = userRecord.language; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source field: user.timezone") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.427+02:00", comments="Source field: user.timezone") public static final SqlColumn timezone = userRecord.timezone; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source field: user.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.427+02:00", comments="Source field: user.active") public static final SqlColumn active = userRecord.active; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.426+02:00", comments="Source Table: user") public static final class UserRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserRecordMapper.java index a80135b6..b2361931 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/UserRecordMapper.java @@ -34,20 +34,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface UserRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.427+02:00", comments="Source Table: user") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.427+02:00", comments="Source Table: user") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.427+02:00", comments="Source Table: user") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -65,7 +65,7 @@ public interface UserRecordMapper { }) UserRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -83,22 +83,22 @@ public interface UserRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.193+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(userRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.194+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, userRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.194+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, userRecord) .where(id, isEqualTo(id_)) @@ -106,7 +106,7 @@ public interface UserRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.194+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") default int insert(UserRecord record) { return insert(SqlBuilder.insert(record) .into(userRecord) @@ -125,7 +125,7 @@ public interface UserRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.194+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") default int insertSelective(UserRecord record) { return insert(SqlBuilder.insert(record) .into(userRecord) @@ -144,19 +144,19 @@ public interface UserRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.194+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, institutionId, uuid, creationDate, name, surname, username, password, email, language, timezone, active) .from(userRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.194+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, institutionId, uuid, creationDate, name, surname, username, password, email, language, timezone, active) .from(userRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.194+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") default UserRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, institutionId, uuid, creationDate, name, surname, username, password, email, language, timezone, active) .from(userRecord) @@ -165,7 +165,7 @@ public interface UserRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.194+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") default UpdateDSL> updateByExample(UserRecord record) { return UpdateDSL.updateWithMapper(this::update, userRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -181,7 +181,7 @@ public interface UserRecordMapper { .set(active).equalTo(record::getActive); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.194+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") default UpdateDSL> updateByExampleSelective(UserRecord record) { return UpdateDSL.updateWithMapper(this::update, userRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) @@ -197,7 +197,7 @@ public interface UserRecordMapper { .set(active).equalToWhenPresent(record::getActive); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.194+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") default int updateByPrimaryKey(UserRecord record) { return UpdateDSL.updateWithMapper(this::update, userRecord) .set(institutionId).equalTo(record::getInstitutionId) @@ -216,7 +216,7 @@ public interface UserRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.194+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.428+02:00", comments="Source Table: user") default int updateByPrimaryKeySelective(UserRecord record) { return UpdateDSL.updateWithMapper(this::update, userRecord) .set(institutionId).equalToWhenPresent(record::getInstitutionId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ViewRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ViewRecordDynamicSqlSupport.java index 7007f1ae..ced58580 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ViewRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ViewRecordDynamicSqlSupport.java @@ -6,25 +6,25 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class ViewRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.100+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.326+02:00", comments="Source Table: view") public static final ViewRecord viewRecord = new ViewRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.100+01:00", comments="Source field: view.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.326+02:00", comments="Source field: view.id") public static final SqlColumn id = viewRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.100+01:00", comments="Source field: view.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.326+02:00", comments="Source field: view.name") public static final SqlColumn name = viewRecord.name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.101+01:00", comments="Source field: view.columns") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.326+02:00", comments="Source field: view.columns") public static final SqlColumn columns = viewRecord.columns; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.101+01:00", comments="Source field: view.position") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.326+02:00", comments="Source field: view.position") public static final SqlColumn position = viewRecord.position; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.101+01:00", comments="Source field: view.template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.327+02:00", comments="Source field: view.template_id") public static final SqlColumn templateId = viewRecord.templateId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.100+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.326+02:00", comments="Source Table: view") public static final class ViewRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ViewRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ViewRecordMapper.java index ef24a82c..5ef83e26 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ViewRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/ViewRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface ViewRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.101+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.327+02:00", comments="Source Table: view") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.102+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.327+02:00", comments="Source Table: view") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.102+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.327+02:00", comments="Source Table: view") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.102+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.327+02:00", comments="Source Table: view") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -56,7 +56,7 @@ public interface ViewRecordMapper { }) ViewRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.102+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.327+02:00", comments="Source Table: view") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -67,22 +67,22 @@ public interface ViewRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.102+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.327+02:00", comments="Source Table: view") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.102+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.327+02:00", comments="Source Table: view") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(viewRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.102+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.327+02:00", comments="Source Table: view") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, viewRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.102+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.327+02:00", comments="Source Table: view") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, viewRecord) .where(id, isEqualTo(id_)) @@ -90,7 +90,7 @@ public interface ViewRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.102+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.328+02:00", comments="Source Table: view") default int insert(ViewRecord record) { return insert(SqlBuilder.insert(record) .into(viewRecord) @@ -102,7 +102,7 @@ public interface ViewRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.102+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.328+02:00", comments="Source Table: view") default int insertSelective(ViewRecord record) { return insert(SqlBuilder.insert(record) .into(viewRecord) @@ -114,19 +114,19 @@ public interface ViewRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.102+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.328+02:00", comments="Source Table: view") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, name, columns, position, templateId) .from(viewRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.102+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.328+02:00", comments="Source Table: view") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, name, columns, position, templateId) .from(viewRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.102+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.328+02:00", comments="Source Table: view") default ViewRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, name, columns, position, templateId) .from(viewRecord) @@ -135,7 +135,7 @@ public interface ViewRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.102+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.328+02:00", comments="Source Table: view") default UpdateDSL> updateByExample(ViewRecord record) { return UpdateDSL.updateWithMapper(this::update, viewRecord) .set(name).equalTo(record::getName) @@ -144,7 +144,7 @@ public interface ViewRecordMapper { .set(templateId).equalTo(record::getTemplateId); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.103+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.328+02:00", comments="Source Table: view") default UpdateDSL> updateByExampleSelective(ViewRecord record) { return UpdateDSL.updateWithMapper(this::update, viewRecord) .set(name).equalToWhenPresent(record::getName) @@ -153,7 +153,7 @@ public interface ViewRecordMapper { .set(templateId).equalToWhenPresent(record::getTemplateId); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.103+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.328+02:00", comments="Source Table: view") default int updateByPrimaryKey(ViewRecord record) { return UpdateDSL.updateWithMapper(this::update, viewRecord) .set(name).equalTo(record::getName) @@ -165,7 +165,7 @@ public interface ViewRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.103+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.329+02:00", comments="Source Table: view") default int updateByPrimaryKeySelective(ViewRecord record) { return UpdateDSL.updateWithMapper(this::update, viewRecord) .set(name).equalToWhenPresent(record::getName) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/WebserviceServerInfoRecordDynamicSqlSupport.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/WebserviceServerInfoRecordDynamicSqlSupport.java index bd2bed6e..a82f3d57 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/WebserviceServerInfoRecordDynamicSqlSupport.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/WebserviceServerInfoRecordDynamicSqlSupport.java @@ -6,25 +6,25 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class WebserviceServerInfoRecordDynamicSqlSupport { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.443+02:00", comments="Source Table: webservice_server_info") public static final WebserviceServerInfoRecord webserviceServerInfoRecord = new WebserviceServerInfoRecord(); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source field: webservice_server_info.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.443+02:00", comments="Source field: webservice_server_info.id") public static final SqlColumn id = webserviceServerInfoRecord.id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source field: webservice_server_info.uuid") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.443+02:00", comments="Source field: webservice_server_info.uuid") public static final SqlColumn uuid = webserviceServerInfoRecord.uuid; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.208+01:00", comments="Source field: webservice_server_info.service_address") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.443+02:00", comments="Source field: webservice_server_info.service_address") public static final SqlColumn serviceAddress = webserviceServerInfoRecord.serviceAddress; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.208+01:00", comments="Source field: webservice_server_info.master") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.443+02:00", comments="Source field: webservice_server_info.master") public static final SqlColumn master = webserviceServerInfoRecord.master; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.208+01:00", comments="Source field: webservice_server_info.update_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.444+02:00", comments="Source field: webservice_server_info.update_time") public static final SqlColumn updateTime = webserviceServerInfoRecord.updateTime; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.443+02:00", comments="Source Table: webservice_server_info") public static final class WebserviceServerInfoRecord extends SqlTable { public final SqlColumn id = column("id", JDBCType.BIGINT); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/WebserviceServerInfoRecordMapper.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/WebserviceServerInfoRecordMapper.java index dd3cc880..6b53c189 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/WebserviceServerInfoRecordMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/mapper/WebserviceServerInfoRecordMapper.java @@ -32,20 +32,20 @@ import org.mybatis.dynamic.sql.util.SqlProviderAdapter; @Mapper public interface WebserviceServerInfoRecordMapper { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.208+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.444+02:00", comments="Source Table: webservice_server_info") @SelectProvider(type=SqlProviderAdapter.class, method="select") long count(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.209+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.444+02:00", comments="Source Table: webservice_server_info") @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.209+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.444+02:00", comments="Source Table: webservice_server_info") @InsertProvider(type=SqlProviderAdapter.class, method="insert") @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class) int insert(InsertStatementProvider insertStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.209+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.444+02:00", comments="Source Table: webservice_server_info") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -56,7 +56,7 @@ public interface WebserviceServerInfoRecordMapper { }) WebserviceServerInfoRecord selectOne(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.209+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.444+02:00", comments="Source Table: webservice_server_info") @SelectProvider(type=SqlProviderAdapter.class, method="select") @ConstructorArgs({ @Arg(column="id", javaType=Long.class, jdbcType=JdbcType.BIGINT, id=true), @@ -67,22 +67,22 @@ public interface WebserviceServerInfoRecordMapper { }) List selectMany(SelectStatementProvider selectStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.210+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.444+02:00", comments="Source Table: webservice_server_info") @UpdateProvider(type=SqlProviderAdapter.class, method="update") int update(UpdateStatementProvider updateStatement); - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.210+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.444+02:00", comments="Source Table: webservice_server_info") default QueryExpressionDSL> countByExample() { return SelectDSL.selectWithMapper(this::count, SqlBuilder.count()) .from(webserviceServerInfoRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.210+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.445+02:00", comments="Source Table: webservice_server_info") default DeleteDSL> deleteByExample() { return DeleteDSL.deleteFromWithMapper(this::delete, webserviceServerInfoRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.210+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.445+02:00", comments="Source Table: webservice_server_info") default int deleteByPrimaryKey(Long id_) { return DeleteDSL.deleteFromWithMapper(this::delete, webserviceServerInfoRecord) .where(id, isEqualTo(id_)) @@ -90,7 +90,7 @@ public interface WebserviceServerInfoRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.210+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.445+02:00", comments="Source Table: webservice_server_info") default int insert(WebserviceServerInfoRecord record) { return insert(SqlBuilder.insert(record) .into(webserviceServerInfoRecord) @@ -102,7 +102,7 @@ public interface WebserviceServerInfoRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.210+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.445+02:00", comments="Source Table: webservice_server_info") default int insertSelective(WebserviceServerInfoRecord record) { return insert(SqlBuilder.insert(record) .into(webserviceServerInfoRecord) @@ -114,19 +114,19 @@ public interface WebserviceServerInfoRecordMapper { .render(RenderingStrategy.MYBATIS3)); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.211+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.445+02:00", comments="Source Table: webservice_server_info") default QueryExpressionDSL>> selectByExample() { return SelectDSL.selectWithMapper(this::selectMany, id, uuid, serviceAddress, master, updateTime) .from(webserviceServerInfoRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.211+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.445+02:00", comments="Source Table: webservice_server_info") default QueryExpressionDSL>> selectDistinctByExample() { return SelectDSL.selectDistinctWithMapper(this::selectMany, id, uuid, serviceAddress, master, updateTime) .from(webserviceServerInfoRecord); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.211+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.445+02:00", comments="Source Table: webservice_server_info") default WebserviceServerInfoRecord selectByPrimaryKey(Long id_) { return SelectDSL.selectWithMapper(this::selectOne, id, uuid, serviceAddress, master, updateTime) .from(webserviceServerInfoRecord) @@ -135,7 +135,7 @@ public interface WebserviceServerInfoRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.211+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.445+02:00", comments="Source Table: webservice_server_info") default UpdateDSL> updateByExample(WebserviceServerInfoRecord record) { return UpdateDSL.updateWithMapper(this::update, webserviceServerInfoRecord) .set(uuid).equalTo(record::getUuid) @@ -144,7 +144,7 @@ public interface WebserviceServerInfoRecordMapper { .set(updateTime).equalTo(record::getUpdateTime); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.211+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.445+02:00", comments="Source Table: webservice_server_info") default UpdateDSL> updateByExampleSelective(WebserviceServerInfoRecord record) { return UpdateDSL.updateWithMapper(this::update, webserviceServerInfoRecord) .set(uuid).equalToWhenPresent(record::getUuid) @@ -153,7 +153,7 @@ public interface WebserviceServerInfoRecordMapper { .set(updateTime).equalToWhenPresent(record::getUpdateTime); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.211+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.445+02:00", comments="Source Table: webservice_server_info") default int updateByPrimaryKey(WebserviceServerInfoRecord record) { return UpdateDSL.updateWithMapper(this::update, webserviceServerInfoRecord) .set(uuid).equalTo(record::getUuid) @@ -165,7 +165,7 @@ public interface WebserviceServerInfoRecordMapper { .execute(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.211+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.445+02:00", comments="Source Table: webservice_server_info") default int updateByPrimaryKeySelective(WebserviceServerInfoRecord record) { return UpdateDSL.updateWithMapper(this::update, webserviceServerInfoRecord) .set(uuid).equalToWhenPresent(record::getUuid) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/AdditionalAttributeRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/AdditionalAttributeRecord.java index 29a36ffa..300d21c4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/AdditionalAttributeRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/AdditionalAttributeRecord.java @@ -3,22 +3,22 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class AdditionalAttributeRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.439+02:00", comments="Source field: additional_attributes.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.entity_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.439+02:00", comments="Source field: additional_attributes.entity_type") private String entityType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.entity_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.439+02:00", comments="Source field: additional_attributes.entity_id") private Long entityId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.439+02:00", comments="Source field: additional_attributes.name") private String name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.439+02:00", comments="Source field: additional_attributes.value") private String value; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.204+01:00", comments="Source Table: additional_attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.439+02:00", comments="Source Table: additional_attributes") public AdditionalAttributeRecord(Long id, String entityType, Long entityId, String name, String value) { this.id = id; this.entityType = entityType; @@ -27,27 +27,27 @@ public class AdditionalAttributeRecord { this.value = value; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.439+02:00", comments="Source field: additional_attributes.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.entity_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.439+02:00", comments="Source field: additional_attributes.entity_type") public String getEntityType() { return entityType; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.entity_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.439+02:00", comments="Source field: additional_attributes.entity_id") public Long getEntityId() { return entityId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.439+02:00", comments="Source field: additional_attributes.name") public String getName() { return name; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.205+01:00", comments="Source field: additional_attributes.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.439+02:00", comments="Source field: additional_attributes.value") public String getValue() { return value; } @@ -56,7 +56,7 @@ public class AdditionalAttributeRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table additional_attributes * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -77,7 +77,7 @@ public class AdditionalAttributeRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table additional_attributes * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -102,7 +102,7 @@ public class AdditionalAttributeRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table additional_attributes * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/BatchActionRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/BatchActionRecord.java index a75a2669..ad07908a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/BatchActionRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/BatchActionRecord.java @@ -3,69 +3,87 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class BatchActionRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source field: batch_action.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source field: batch_action.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.institution_id") private Long institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source field: batch_action.action_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.owner") + private String owner; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.action_type") private String actionType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source field: batch_action.source_ids") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.attributes") + private String attributes; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.source_ids") private String sourceIds; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.218+01:00", comments="Source field: batch_action.successful") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.successful") private String successful; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.218+01:00", comments="Source field: batch_action.last_update") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.last_update") private Long lastUpdate; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.218+01:00", comments="Source field: batch_action.processor_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.processor_id") private String processorId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source Table: batch_action") - public BatchActionRecord(Long id, Long institutionId, String actionType, String sourceIds, String successful, Long lastUpdate, String processorId) { + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source Table: batch_action") + public BatchActionRecord(Long id, Long institutionId, String owner, String actionType, String attributes, String sourceIds, String successful, Long lastUpdate, String processorId) { this.id = id; this.institutionId = institutionId; + this.owner = owner; this.actionType = actionType; + this.attributes = attributes; this.sourceIds = sourceIds; this.successful = successful; this.lastUpdate = lastUpdate; this.processorId = processorId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source field: batch_action.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source field: batch_action.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.institution_id") public Long getInstitutionId() { return institutionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.217+01:00", comments="Source field: batch_action.action_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.owner") + public String getOwner() { + return owner; + } + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.action_type") public String getActionType() { return actionType; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.218+01:00", comments="Source field: batch_action.source_ids") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.attributes") + public String getAttributes() { + return attributes; + } + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.source_ids") public String getSourceIds() { return sourceIds; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.218+01:00", comments="Source field: batch_action.successful") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.successful") public String getSuccessful() { return successful; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.218+01:00", comments="Source field: batch_action.last_update") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.last_update") public Long getLastUpdate() { return lastUpdate; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.218+01:00", comments="Source field: batch_action.processor_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.453+02:00", comments="Source field: batch_action.processor_id") public String getProcessorId() { return processorId; } @@ -74,7 +92,7 @@ public class BatchActionRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table batch_action * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -84,7 +102,9 @@ public class BatchActionRecord { sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", institutionId=").append(institutionId); + sb.append(", owner=").append(owner); sb.append(", actionType=").append(actionType); + sb.append(", attributes=").append(attributes); sb.append(", sourceIds=").append(sourceIds); sb.append(", successful=").append(successful); sb.append(", lastUpdate=").append(lastUpdate); @@ -97,7 +117,7 @@ public class BatchActionRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table batch_action * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -113,7 +133,9 @@ public class BatchActionRecord { BatchActionRecord other = (BatchActionRecord) that; return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId())) && (this.getInstitutionId() == null ? other.getInstitutionId() == null : this.getInstitutionId().equals(other.getInstitutionId())) + && (this.getOwner() == null ? other.getOwner() == null : this.getOwner().equals(other.getOwner())) && (this.getActionType() == null ? other.getActionType() == null : this.getActionType().equals(other.getActionType())) + && (this.getAttributes() == null ? other.getAttributes() == null : this.getAttributes().equals(other.getAttributes())) && (this.getSourceIds() == null ? other.getSourceIds() == null : this.getSourceIds().equals(other.getSourceIds())) && (this.getSuccessful() == null ? other.getSuccessful() == null : this.getSuccessful().equals(other.getSuccessful())) && (this.getLastUpdate() == null ? other.getLastUpdate() == null : this.getLastUpdate().equals(other.getLastUpdate())) @@ -124,7 +146,7 @@ public class BatchActionRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table batch_action * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { @@ -132,7 +154,9 @@ public class BatchActionRecord { int result = 1; result = prime * result + ((getId() == null) ? 0 : getId().hashCode()); result = prime * result + ((getInstitutionId() == null) ? 0 : getInstitutionId().hashCode()); + result = prime * result + ((getOwner() == null) ? 0 : getOwner().hashCode()); result = prime * result + ((getActionType() == null) ? 0 : getActionType().hashCode()); + result = prime * result + ((getAttributes() == null) ? 0 : getAttributes().hashCode()); result = prime * result + ((getSourceIds() == null) ? 0 : getSourceIds().hashCode()); result = prime * result + ((getSuccessful() == null) ? 0 : getSuccessful().hashCode()); result = prime * result + ((getLastUpdate() == null) ? 0 : getLastUpdate().hashCode()); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/CertificateRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/CertificateRecord.java index c70276ce..e5584423 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/CertificateRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/CertificateRecord.java @@ -4,19 +4,19 @@ import java.util.Arrays; import javax.annotation.Generated; public class CertificateRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.212+01:00", comments="Source field: certificate.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.446+02:00", comments="Source field: certificate.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.212+01:00", comments="Source field: certificate.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.446+02:00", comments="Source field: certificate.institution_id") private Long institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.212+01:00", comments="Source field: certificate.aliases") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.446+02:00", comments="Source field: certificate.aliases") private String aliases; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.212+01:00", comments="Source field: certificate.cert_store") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.446+02:00", comments="Source field: certificate.cert_store") private byte[] certStore; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.212+01:00", comments="Source Table: certificate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.446+02:00", comments="Source Table: certificate") public CertificateRecord(Long id, Long institutionId, String aliases, byte[] certStore) { this.id = id; this.institutionId = institutionId; @@ -24,22 +24,22 @@ public class CertificateRecord { this.certStore = certStore; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.212+01:00", comments="Source field: certificate.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.446+02:00", comments="Source field: certificate.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.212+01:00", comments="Source field: certificate.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.446+02:00", comments="Source field: certificate.institution_id") public Long getInstitutionId() { return institutionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.212+01:00", comments="Source field: certificate.aliases") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.446+02:00", comments="Source field: certificate.aliases") public String getAliases() { return aliases; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.212+01:00", comments="Source field: certificate.cert_store") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.446+02:00", comments="Source field: certificate.cert_store") public byte[] getCertStore() { return certStore; } @@ -48,7 +48,7 @@ public class CertificateRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table certificate * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -68,7 +68,7 @@ public class CertificateRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table certificate * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -92,7 +92,7 @@ public class CertificateRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table certificate * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientConnectionRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientConnectionRecord.java index 8b36c685..04558333 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientConnectionRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientConnectionRecord.java @@ -3,58 +3,58 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class ClientConnectionRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.138+01:00", comments="Source field: client_connection.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.370+02:00", comments="Source field: client_connection.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.138+01:00", comments="Source field: client_connection.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.370+02:00", comments="Source field: client_connection.institution_id") private Long institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.370+02:00", comments="Source field: client_connection.exam_id") private Long examId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.status") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.370+02:00", comments="Source field: client_connection.status") private String status; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.connection_token") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.connection_token") private String connectionToken; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.exam_user_session_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.exam_user_session_id") private String examUserSessionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.client_address") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.client_address") private String clientAddress; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.virtual_client_address") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.virtual_client_address") private String virtualClientAddress; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.vdi") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.vdi") private Integer vdi; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.vdi_pair_token") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.vdi_pair_token") private String vdiPairToken; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.creation_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.creation_time") private Long creationTime; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.update_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.update_time") private Long updateTime; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.remote_proctoring_room_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.remote_proctoring_room_id") private Long remoteProctoringRoomId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.remote_proctoring_room_update") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.remote_proctoring_room_update") private Integer remoteProctoringRoomUpdate; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.client_machine_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.372+02:00", comments="Source field: client_connection.client_machine_name") private String clientMachineName; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.client_os_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.372+02:00", comments="Source field: client_connection.client_os_name") private String clientOsName; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.client_version") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.372+02:00", comments="Source field: client_connection.client_version") private String clientVersion; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.138+01:00", comments="Source Table: client_connection") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.370+02:00", comments="Source Table: client_connection") public ClientConnectionRecord(Long id, Long institutionId, Long examId, String status, String connectionToken, String examUserSessionId, String clientAddress, String virtualClientAddress, Integer vdi, String vdiPairToken, Long creationTime, Long updateTime, Long remoteProctoringRoomId, Integer remoteProctoringRoomUpdate, String clientMachineName, String clientOsName, String clientVersion) { this.id = id; this.institutionId = institutionId; @@ -75,87 +75,87 @@ public class ClientConnectionRecord { this.clientVersion = clientVersion; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.138+01:00", comments="Source field: client_connection.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.370+02:00", comments="Source field: client_connection.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.370+02:00", comments="Source field: client_connection.institution_id") public Long getInstitutionId() { return institutionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.370+02:00", comments="Source field: client_connection.exam_id") public Long getExamId() { return examId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.status") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.status") public String getStatus() { return status; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.connection_token") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.connection_token") public String getConnectionToken() { return connectionToken; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.exam_user_session_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.exam_user_session_id") public String getExamUserSessionId() { return examUserSessionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.client_address") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.client_address") public String getClientAddress() { return clientAddress; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.virtual_client_address") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.virtual_client_address") public String getVirtualClientAddress() { return virtualClientAddress; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.vdi") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.vdi") public Integer getVdi() { return vdi; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.139+01:00", comments="Source field: client_connection.vdi_pair_token") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.vdi_pair_token") public String getVdiPairToken() { return vdiPairToken; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.creation_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.creation_time") public Long getCreationTime() { return creationTime; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.update_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.update_time") public Long getUpdateTime() { return updateTime; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.remote_proctoring_room_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.remote_proctoring_room_id") public Long getRemoteProctoringRoomId() { return remoteProctoringRoomId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.remote_proctoring_room_update") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.371+02:00", comments="Source field: client_connection.remote_proctoring_room_update") public Integer getRemoteProctoringRoomUpdate() { return remoteProctoringRoomUpdate; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.client_machine_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.372+02:00", comments="Source field: client_connection.client_machine_name") public String getClientMachineName() { return clientMachineName; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.client_os_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.372+02:00", comments="Source field: client_connection.client_os_name") public String getClientOsName() { return clientOsName; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.140+01:00", comments="Source field: client_connection.client_version") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.372+02:00", comments="Source field: client_connection.client_version") public String getClientVersion() { return clientVersion; } @@ -164,7 +164,7 @@ public class ClientConnectionRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_connection * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -197,7 +197,7 @@ public class ClientConnectionRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_connection * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -234,7 +234,7 @@ public class ClientConnectionRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_connection * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientEventRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientEventRecord.java index f2c79f3e..fa0f3125 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientEventRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientEventRecord.java @@ -4,28 +4,28 @@ import java.math.BigDecimal; import javax.annotation.Generated; public class ClientEventRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.151+01:00", comments="Source field: client_event.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.385+02:00", comments="Source field: client_event.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.151+01:00", comments="Source field: client_event.client_connection_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.client_connection_id") private Long clientConnectionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.151+01:00", comments="Source field: client_event.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.type") private Integer type; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.151+01:00", comments="Source field: client_event.client_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.client_time") private Long clientTime; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.152+01:00", comments="Source field: client_event.server_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.server_time") private Long serverTime; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.152+01:00", comments="Source field: client_event.numeric_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.numeric_value") private BigDecimal numericValue; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.152+01:00", comments="Source field: client_event.text") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.text") private String text; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.151+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.385+02:00", comments="Source Table: client_event") public ClientEventRecord(Long id, Long clientConnectionId, Integer type, Long clientTime, Long serverTime, BigDecimal numericValue, String text) { this.id = id; this.clientConnectionId = clientConnectionId; @@ -36,77 +36,77 @@ public class ClientEventRecord { this.text = text; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.151+01:00", comments="Source Table: client_event") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.385+02:00", comments="Source Table: client_event") public ClientEventRecord() { super(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.151+01:00", comments="Source field: client_event.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.385+02:00", comments="Source field: client_event.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.151+01:00", comments="Source field: client_event.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.id") public void setId(Long id) { this.id = id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.151+01:00", comments="Source field: client_event.client_connection_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.client_connection_id") public Long getClientConnectionId() { return clientConnectionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.151+01:00", comments="Source field: client_event.client_connection_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.client_connection_id") public void setClientConnectionId(Long clientConnectionId) { this.clientConnectionId = clientConnectionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.151+01:00", comments="Source field: client_event.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.type") public Integer getType() { return type; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.151+01:00", comments="Source field: client_event.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.type") public void setType(Integer type) { this.type = type; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.151+01:00", comments="Source field: client_event.client_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.client_time") public Long getClientTime() { return clientTime; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.152+01:00", comments="Source field: client_event.client_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.client_time") public void setClientTime(Long clientTime) { this.clientTime = clientTime; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.152+01:00", comments="Source field: client_event.server_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.server_time") public Long getServerTime() { return serverTime; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.152+01:00", comments="Source field: client_event.server_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.server_time") public void setServerTime(Long serverTime) { this.serverTime = serverTime; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.152+01:00", comments="Source field: client_event.numeric_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.numeric_value") public BigDecimal getNumericValue() { return numericValue; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.152+01:00", comments="Source field: client_event.numeric_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.386+02:00", comments="Source field: client_event.numeric_value") public void setNumericValue(BigDecimal numericValue) { this.numericValue = numericValue; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.152+01:00", comments="Source field: client_event.text") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.387+02:00", comments="Source field: client_event.text") public String getText() { return text; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.152+01:00", comments="Source field: client_event.text") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.387+02:00", comments="Source field: client_event.text") public void setText(String text) { this.text = text == null ? null : text.trim(); } @@ -115,7 +115,7 @@ public class ClientEventRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_event * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -138,7 +138,7 @@ public class ClientEventRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_event * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -165,7 +165,7 @@ public class ClientEventRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_event * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientIndicatorRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientIndicatorRecord.java index a16fc622..49f8dfc3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientIndicatorRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientIndicatorRecord.java @@ -3,19 +3,19 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class ClientIndicatorRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source field: client_indicator.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.456+02:00", comments="Source field: client_indicator.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source field: client_indicator.client_connection_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.456+02:00", comments="Source field: client_indicator.client_connection_id") private Long clientConnectionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source field: client_indicator.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.456+02:00", comments="Source field: client_indicator.type") private Integer type; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source field: client_indicator.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source field: client_indicator.value") private Long value; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.456+02:00", comments="Source Table: client_indicator") public ClientIndicatorRecord(Long id, Long clientConnectionId, Integer type, Long value) { this.id = id; this.clientConnectionId = clientConnectionId; @@ -23,47 +23,47 @@ public class ClientIndicatorRecord { this.value = value; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source Table: client_indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.456+02:00", comments="Source Table: client_indicator") public ClientIndicatorRecord() { super(); } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source field: client_indicator.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.456+02:00", comments="Source field: client_indicator.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source field: client_indicator.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.456+02:00", comments="Source field: client_indicator.id") public void setId(Long id) { this.id = id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source field: client_indicator.client_connection_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.456+02:00", comments="Source field: client_indicator.client_connection_id") public Long getClientConnectionId() { return clientConnectionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.220+01:00", comments="Source field: client_indicator.client_connection_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.456+02:00", comments="Source field: client_indicator.client_connection_id") public void setClientConnectionId(Long clientConnectionId) { this.clientConnectionId = clientConnectionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source field: client_indicator.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.456+02:00", comments="Source field: client_indicator.type") public Integer getType() { return type; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source field: client_indicator.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source field: client_indicator.type") public void setType(Integer type) { this.type = type; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source field: client_indicator.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source field: client_indicator.value") public Long getValue() { return value; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.221+01:00", comments="Source field: client_indicator.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.457+02:00", comments="Source field: client_indicator.value") public void setValue(Long value) { this.value = value; } @@ -72,7 +72,7 @@ public class ClientIndicatorRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_indicator * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -92,7 +92,7 @@ public class ClientIndicatorRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_indicator * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -116,7 +116,7 @@ public class ClientIndicatorRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_indicator * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientInstructionRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientInstructionRecord.java index 09909460..5ee262aa 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientInstructionRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientInstructionRecord.java @@ -3,28 +3,28 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class ClientInstructionRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.158+01:00", comments="Source field: client_instruction.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source field: client_instruction.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.158+01:00", comments="Source field: client_instruction.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source field: client_instruction.exam_id") private Long examId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.159+01:00", comments="Source field: client_instruction.connection_token") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source field: client_instruction.connection_token") private String connectionToken; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source field: client_instruction.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source field: client_instruction.type") private String type; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source field: client_instruction.attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source field: client_instruction.attributes") private String attributes; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source field: client_instruction.needs_confirmation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source field: client_instruction.needs_confirmation") private Integer needsConfirmation; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source field: client_instruction.timestamp") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source field: client_instruction.timestamp") private Long timestamp; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.158+01:00", comments="Source Table: client_instruction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source Table: client_instruction") public ClientInstructionRecord(Long id, Long examId, String connectionToken, String type, String attributes, Integer needsConfirmation, Long timestamp) { this.id = id; this.examId = examId; @@ -35,37 +35,37 @@ public class ClientInstructionRecord { this.timestamp = timestamp; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.158+01:00", comments="Source field: client_instruction.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source field: client_instruction.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.159+01:00", comments="Source field: client_instruction.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source field: client_instruction.exam_id") public Long getExamId() { return examId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.159+01:00", comments="Source field: client_instruction.connection_token") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source field: client_instruction.connection_token") public String getConnectionToken() { return connectionToken; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source field: client_instruction.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source field: client_instruction.type") public String getType() { return type; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source field: client_instruction.attributes") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source field: client_instruction.attributes") public String getAttributes() { return attributes; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source field: client_instruction.needs_confirmation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source field: client_instruction.needs_confirmation") public Integer getNeedsConfirmation() { return needsConfirmation; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.161+01:00", comments="Source field: client_instruction.timestamp") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.391+02:00", comments="Source field: client_instruction.timestamp") public Long getTimestamp() { return timestamp; } @@ -74,7 +74,7 @@ public class ClientInstructionRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_instruction * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -97,7 +97,7 @@ public class ClientInstructionRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_instruction * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -124,7 +124,7 @@ public class ClientInstructionRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_instruction * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientNotificationRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientNotificationRecord.java index b93c974c..d86802ea 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientNotificationRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ClientNotificationRecord.java @@ -3,25 +3,25 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class ClientNotificationRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source field: client_notification.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.459+02:00", comments="Source field: client_notification.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source field: client_notification.client_connection_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.459+02:00", comments="Source field: client_notification.client_connection_id") private Long clientConnectionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source field: client_notification.event_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.459+02:00", comments="Source field: client_notification.event_type") private Integer eventType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source field: client_notification.notification_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.459+02:00", comments="Source field: client_notification.notification_type") private Integer notificationType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source field: client_notification.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.459+02:00", comments="Source field: client_notification.value") private Long value; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source field: client_notification.text") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.459+02:00", comments="Source field: client_notification.text") private String text; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source Table: client_notification") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.459+02:00", comments="Source Table: client_notification") public ClientNotificationRecord(Long id, Long clientConnectionId, Integer eventType, Integer notificationType, Long value, String text) { this.id = id; this.clientConnectionId = clientConnectionId; @@ -31,32 +31,32 @@ public class ClientNotificationRecord { this.text = text; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source field: client_notification.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.459+02:00", comments="Source field: client_notification.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source field: client_notification.client_connection_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.459+02:00", comments="Source field: client_notification.client_connection_id") public Long getClientConnectionId() { return clientConnectionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source field: client_notification.event_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.459+02:00", comments="Source field: client_notification.event_type") public Integer getEventType() { return eventType; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source field: client_notification.notification_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.459+02:00", comments="Source field: client_notification.notification_type") public Integer getNotificationType() { return notificationType; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source field: client_notification.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.459+02:00", comments="Source field: client_notification.value") public Long getValue() { return value; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.222+01:00", comments="Source field: client_notification.text") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.459+02:00", comments="Source field: client_notification.text") public String getText() { return text; } @@ -65,7 +65,7 @@ public class ClientNotificationRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_notification * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -87,7 +87,7 @@ public class ClientNotificationRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_notification * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -113,7 +113,7 @@ public class ClientNotificationRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table client_notification * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationAttributeRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationAttributeRecord.java index df1110e7..cddc3d26 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationAttributeRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationAttributeRecord.java @@ -3,31 +3,31 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class ConfigurationAttributeRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.934+01:00", comments="Source field: configuration_attribute.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.146+02:00", comments="Source field: configuration_attribute.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.935+01:00", comments="Source field: configuration_attribute.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.147+02:00", comments="Source field: configuration_attribute.name") private String name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.935+01:00", comments="Source field: configuration_attribute.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.148+02:00", comments="Source field: configuration_attribute.type") private String type; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.935+01:00", comments="Source field: configuration_attribute.parent_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.148+02:00", comments="Source field: configuration_attribute.parent_id") private Long parentId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.935+01:00", comments="Source field: configuration_attribute.resources") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.148+02:00", comments="Source field: configuration_attribute.resources") private String resources; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.935+01:00", comments="Source field: configuration_attribute.validator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.148+02:00", comments="Source field: configuration_attribute.validator") private String validator; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.935+01:00", comments="Source field: configuration_attribute.dependencies") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.148+02:00", comments="Source field: configuration_attribute.dependencies") private String dependencies; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.935+01:00", comments="Source field: configuration_attribute.default_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.149+02:00", comments="Source field: configuration_attribute.default_value") private String defaultValue; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.929+01:00", comments="Source Table: configuration_attribute") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.141+02:00", comments="Source Table: configuration_attribute") public ConfigurationAttributeRecord(Long id, String name, String type, Long parentId, String resources, String validator, String dependencies, String defaultValue) { this.id = id; this.name = name; @@ -39,42 +39,42 @@ public class ConfigurationAttributeRecord { this.defaultValue = defaultValue; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.934+01:00", comments="Source field: configuration_attribute.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.147+02:00", comments="Source field: configuration_attribute.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.935+01:00", comments="Source field: configuration_attribute.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.147+02:00", comments="Source field: configuration_attribute.name") public String getName() { return name; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.935+01:00", comments="Source field: configuration_attribute.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.148+02:00", comments="Source field: configuration_attribute.type") public String getType() { return type; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.935+01:00", comments="Source field: configuration_attribute.parent_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.148+02:00", comments="Source field: configuration_attribute.parent_id") public Long getParentId() { return parentId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.935+01:00", comments="Source field: configuration_attribute.resources") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.148+02:00", comments="Source field: configuration_attribute.resources") public String getResources() { return resources; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.935+01:00", comments="Source field: configuration_attribute.validator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.148+02:00", comments="Source field: configuration_attribute.validator") public String getValidator() { return validator; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.935+01:00", comments="Source field: configuration_attribute.dependencies") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.148+02:00", comments="Source field: configuration_attribute.dependencies") public String getDependencies() { return dependencies; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:20.935+01:00", comments="Source field: configuration_attribute.default_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.149+02:00", comments="Source field: configuration_attribute.default_value") public String getDefaultValue() { return defaultValue; } @@ -83,7 +83,7 @@ public class ConfigurationAttributeRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table configuration_attribute * - * @mbg.generated Tue Jan 18 17:36:20 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -107,7 +107,7 @@ public class ConfigurationAttributeRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table configuration_attribute * - * @mbg.generated Tue Jan 18 17:36:20 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -135,7 +135,7 @@ public class ConfigurationAttributeRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table configuration_attribute * - * @mbg.generated Tue Jan 18 17:36:20 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationNodeRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationNodeRecord.java index 7517d40d..dc021099 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationNodeRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationNodeRecord.java @@ -3,32 +3,38 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class ConfigurationNodeRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.117+01:00", comments="Source field: configuration_node.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.342+02:00", comments="Source field: configuration_node.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.117+01:00", comments="Source field: configuration_node.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.342+02:00", comments="Source field: configuration_node.institution_id") private Long institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.118+01:00", comments="Source field: configuration_node.template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.342+02:00", comments="Source field: configuration_node.template_id") private Long templateId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.118+01:00", comments="Source field: configuration_node.owner") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.342+02:00", comments="Source field: configuration_node.owner") private String owner; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.118+01:00", comments="Source field: configuration_node.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.342+02:00", comments="Source field: configuration_node.name") private String name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.119+01:00", comments="Source field: configuration_node.description") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.343+02:00", comments="Source field: configuration_node.description") private String description; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.119+01:00", comments="Source field: configuration_node.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.343+02:00", comments="Source field: configuration_node.type") private String type; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.119+01:00", comments="Source field: configuration_node.status") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.343+02:00", comments="Source field: configuration_node.status") private String status; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.117+01:00", comments="Source Table: configuration_node") - public ConfigurationNodeRecord(Long id, Long institutionId, Long templateId, String owner, String name, String description, String type, String status) { + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.343+02:00", comments="Source field: configuration_node.last_update_time") + private Long lastUpdateTime; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.343+02:00", comments="Source field: configuration_node.last_update_user") + private String lastUpdateUser; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.342+02:00", comments="Source Table: configuration_node") + public ConfigurationNodeRecord(Long id, Long institutionId, Long templateId, String owner, String name, String description, String type, String status, Long lastUpdateTime, String lastUpdateUser) { this.id = id; this.institutionId = institutionId; this.templateId = templateId; @@ -37,53 +43,65 @@ public class ConfigurationNodeRecord { this.description = description; this.type = type; this.status = status; + this.lastUpdateTime = lastUpdateTime; + this.lastUpdateUser = lastUpdateUser; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.117+01:00", comments="Source field: configuration_node.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.342+02:00", comments="Source field: configuration_node.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.117+01:00", comments="Source field: configuration_node.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.342+02:00", comments="Source field: configuration_node.institution_id") public Long getInstitutionId() { return institutionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.118+01:00", comments="Source field: configuration_node.template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.342+02:00", comments="Source field: configuration_node.template_id") public Long getTemplateId() { return templateId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.118+01:00", comments="Source field: configuration_node.owner") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.342+02:00", comments="Source field: configuration_node.owner") public String getOwner() { return owner; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.118+01:00", comments="Source field: configuration_node.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.343+02:00", comments="Source field: configuration_node.name") public String getName() { return name; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.119+01:00", comments="Source field: configuration_node.description") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.343+02:00", comments="Source field: configuration_node.description") public String getDescription() { return description; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.119+01:00", comments="Source field: configuration_node.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.343+02:00", comments="Source field: configuration_node.type") public String getType() { return type; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.119+01:00", comments="Source field: configuration_node.status") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.343+02:00", comments="Source field: configuration_node.status") public String getStatus() { return status; } + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.343+02:00", comments="Source field: configuration_node.last_update_time") + public Long getLastUpdateTime() { + return lastUpdateTime; + } + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.343+02:00", comments="Source field: configuration_node.last_update_user") + public String getLastUpdateUser() { + return lastUpdateUser; + } + /** * This method was generated by MyBatis Generator. * This method corresponds to the database table configuration_node * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -99,6 +117,8 @@ public class ConfigurationNodeRecord { sb.append(", description=").append(description); sb.append(", type=").append(type); sb.append(", status=").append(status); + sb.append(", lastUpdateTime=").append(lastUpdateTime); + sb.append(", lastUpdateUser=").append(lastUpdateUser); sb.append("]"); return sb.toString(); } @@ -107,7 +127,7 @@ public class ConfigurationNodeRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table configuration_node * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -128,14 +148,16 @@ public class ConfigurationNodeRecord { && (this.getName() == null ? other.getName() == null : this.getName().equals(other.getName())) && (this.getDescription() == null ? other.getDescription() == null : this.getDescription().equals(other.getDescription())) && (this.getType() == null ? other.getType() == null : this.getType().equals(other.getType())) - && (this.getStatus() == null ? other.getStatus() == null : this.getStatus().equals(other.getStatus())); + && (this.getStatus() == null ? other.getStatus() == null : this.getStatus().equals(other.getStatus())) + && (this.getLastUpdateTime() == null ? other.getLastUpdateTime() == null : this.getLastUpdateTime().equals(other.getLastUpdateTime())) + && (this.getLastUpdateUser() == null ? other.getLastUpdateUser() == null : this.getLastUpdateUser().equals(other.getLastUpdateUser())); } /** * This method was generated by MyBatis Generator. * This method corresponds to the database table configuration_node * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { @@ -149,6 +171,8 @@ public class ConfigurationNodeRecord { result = prime * result + ((getDescription() == null) ? 0 : getDescription().hashCode()); result = prime * result + ((getType() == null) ? 0 : getType().hashCode()); result = prime * result + ((getStatus() == null) ? 0 : getStatus().hashCode()); + result = prime * result + ((getLastUpdateTime() == null) ? 0 : getLastUpdateTime().hashCode()); + result = prime * result + ((getLastUpdateUser() == null) ? 0 : getLastUpdateUser().hashCode()); return result; } } \ No newline at end of file diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationRecord.java index 7ae23f6a..f133c8d2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationRecord.java @@ -4,25 +4,25 @@ import javax.annotation.Generated; import org.joda.time.DateTime; public class ConfigurationRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.109+01:00", comments="Source field: configuration.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.337+02:00", comments="Source field: configuration.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.111+01:00", comments="Source field: configuration.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.337+02:00", comments="Source field: configuration.institution_id") private Long institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.112+01:00", comments="Source field: configuration.configuration_node_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.337+02:00", comments="Source field: configuration.configuration_node_id") private Long configurationNodeId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.112+01:00", comments="Source field: configuration.version") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.337+02:00", comments="Source field: configuration.version") private String version; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.112+01:00", comments="Source field: configuration.version_date") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.337+02:00", comments="Source field: configuration.version_date") private DateTime versionDate; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.112+01:00", comments="Source field: configuration.followup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.337+02:00", comments="Source field: configuration.followup") private Integer followup; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.109+01:00", comments="Source Table: configuration") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.337+02:00", comments="Source Table: configuration") public ConfigurationRecord(Long id, Long institutionId, Long configurationNodeId, String version, DateTime versionDate, Integer followup) { this.id = id; this.institutionId = institutionId; @@ -32,32 +32,32 @@ public class ConfigurationRecord { this.followup = followup; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.109+01:00", comments="Source field: configuration.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.337+02:00", comments="Source field: configuration.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.112+01:00", comments="Source field: configuration.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.337+02:00", comments="Source field: configuration.institution_id") public Long getInstitutionId() { return institutionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.112+01:00", comments="Source field: configuration.configuration_node_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.337+02:00", comments="Source field: configuration.configuration_node_id") public Long getConfigurationNodeId() { return configurationNodeId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.112+01:00", comments="Source field: configuration.version") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.337+02:00", comments="Source field: configuration.version") public String getVersion() { return version; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.112+01:00", comments="Source field: configuration.version_date") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.337+02:00", comments="Source field: configuration.version_date") public DateTime getVersionDate() { return versionDate; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.112+01:00", comments="Source field: configuration.followup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.338+02:00", comments="Source field: configuration.followup") public Integer getFollowup() { return followup; } @@ -66,7 +66,7 @@ public class ConfigurationRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table configuration * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -88,7 +88,7 @@ public class ConfigurationRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table configuration * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -114,7 +114,7 @@ public class ConfigurationRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table configuration * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationValueRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationValueRecord.java index 59f4d268..3e7f0744 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationValueRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ConfigurationValueRecord.java @@ -3,25 +3,25 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class ConfigurationValueRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.094+01:00", comments="Source field: configuration_value.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.318+02:00", comments="Source field: configuration_value.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.094+01:00", comments="Source field: configuration_value.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.318+02:00", comments="Source field: configuration_value.institution_id") private Long institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.094+01:00", comments="Source field: configuration_value.configuration_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.319+02:00", comments="Source field: configuration_value.configuration_id") private Long configurationId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.095+01:00", comments="Source field: configuration_value.configuration_attribute_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.319+02:00", comments="Source field: configuration_value.configuration_attribute_id") private Long configurationAttributeId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.095+01:00", comments="Source field: configuration_value.list_index") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.319+02:00", comments="Source field: configuration_value.list_index") private Integer listIndex; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.095+01:00", comments="Source field: configuration_value.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.319+02:00", comments="Source field: configuration_value.value") private String value; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.094+01:00", comments="Source Table: configuration_value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.318+02:00", comments="Source Table: configuration_value") public ConfigurationValueRecord(Long id, Long institutionId, Long configurationId, Long configurationAttributeId, Integer listIndex, String value) { this.id = id; this.institutionId = institutionId; @@ -31,32 +31,32 @@ public class ConfigurationValueRecord { this.value = value; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.094+01:00", comments="Source field: configuration_value.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.318+02:00", comments="Source field: configuration_value.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.094+01:00", comments="Source field: configuration_value.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.319+02:00", comments="Source field: configuration_value.institution_id") public Long getInstitutionId() { return institutionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.095+01:00", comments="Source field: configuration_value.configuration_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.319+02:00", comments="Source field: configuration_value.configuration_id") public Long getConfigurationId() { return configurationId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.095+01:00", comments="Source field: configuration_value.configuration_attribute_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.319+02:00", comments="Source field: configuration_value.configuration_attribute_id") public Long getConfigurationAttributeId() { return configurationAttributeId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.095+01:00", comments="Source field: configuration_value.list_index") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.319+02:00", comments="Source field: configuration_value.list_index") public Integer getListIndex() { return listIndex; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.095+01:00", comments="Source field: configuration_value.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.319+02:00", comments="Source field: configuration_value.value") public String getValue() { return value; } @@ -65,7 +65,7 @@ public class ConfigurationValueRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table configuration_value * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -87,7 +87,7 @@ public class ConfigurationValueRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table configuration_value * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -113,7 +113,7 @@ public class ConfigurationValueRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table configuration_value * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ExamConfigurationMapRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ExamConfigurationMapRecord.java index a1961167..69b19aa9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ExamConfigurationMapRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ExamConfigurationMapRecord.java @@ -3,25 +3,25 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class ExamConfigurationMapRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.125+01:00", comments="Source field: exam_configuration_map.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.351+02:00", comments="Source field: exam_configuration_map.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.125+01:00", comments="Source field: exam_configuration_map.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.353+02:00", comments="Source field: exam_configuration_map.institution_id") private Long institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.125+01:00", comments="Source field: exam_configuration_map.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.353+02:00", comments="Source field: exam_configuration_map.exam_id") private Long examId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.125+01:00", comments="Source field: exam_configuration_map.configuration_node_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.354+02:00", comments="Source field: exam_configuration_map.configuration_node_id") private Long configurationNodeId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.125+01:00", comments="Source field: exam_configuration_map.user_names") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.354+02:00", comments="Source field: exam_configuration_map.user_names") private String userNames; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.125+01:00", comments="Source field: exam_configuration_map.encrypt_secret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.354+02:00", comments="Source field: exam_configuration_map.encrypt_secret") private String encryptSecret; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.125+01:00", comments="Source Table: exam_configuration_map") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.351+02:00", comments="Source Table: exam_configuration_map") public ExamConfigurationMapRecord(Long id, Long institutionId, Long examId, Long configurationNodeId, String userNames, String encryptSecret) { this.id = id; this.institutionId = institutionId; @@ -31,32 +31,32 @@ public class ExamConfigurationMapRecord { this.encryptSecret = encryptSecret; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.125+01:00", comments="Source field: exam_configuration_map.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.353+02:00", comments="Source field: exam_configuration_map.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.125+01:00", comments="Source field: exam_configuration_map.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.353+02:00", comments="Source field: exam_configuration_map.institution_id") public Long getInstitutionId() { return institutionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.125+01:00", comments="Source field: exam_configuration_map.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.353+02:00", comments="Source field: exam_configuration_map.exam_id") public Long getExamId() { return examId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.125+01:00", comments="Source field: exam_configuration_map.configuration_node_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.354+02:00", comments="Source field: exam_configuration_map.configuration_node_id") public Long getConfigurationNodeId() { return configurationNodeId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.125+01:00", comments="Source field: exam_configuration_map.user_names") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.354+02:00", comments="Source field: exam_configuration_map.user_names") public String getUserNames() { return userNames; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.125+01:00", comments="Source field: exam_configuration_map.encrypt_secret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.354+02:00", comments="Source field: exam_configuration_map.encrypt_secret") public String getEncryptSecret() { return encryptSecret; } @@ -65,7 +65,7 @@ public class ExamConfigurationMapRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table exam_configuration_map * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -87,7 +87,7 @@ public class ExamConfigurationMapRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table exam_configuration_map * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -113,7 +113,7 @@ public class ExamConfigurationMapRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table exam_configuration_map * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ExamRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ExamRecord.java index cb1ba02f..fbe8f0df 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ExamRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ExamRecord.java @@ -1,58 +1,71 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; +import org.joda.time.DateTime; public class ExamRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.359+02:00", comments="Source field: exam.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.359+02:00", comments="Source field: exam.institution_id") private Long institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.lms_setup_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.359+02:00", comments="Source field: exam.lms_setup_id") private Long lmsSetupId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.external_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.359+02:00", comments="Source field: exam.external_id") private String externalId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.owner") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.359+02:00", comments="Source field: exam.owner") private String owner; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.supporter") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.359+02:00", comments="Source field: exam.supporter") private String supporter; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.type") private String type; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.quit_password") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.quit_password") private String quitPassword; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.131+01:00", comments="Source field: exam.browser_keys") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.browser_keys") private String browserKeys; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.131+01:00", comments="Source field: exam.status") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.status") private String status; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.131+01:00", comments="Source field: exam.lms_seb_restriction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.lms_seb_restriction") private Integer lmsSebRestriction; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.131+01:00", comments="Source field: exam.updating") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.updating") private Integer updating; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.131+01:00", comments="Source field: exam.lastupdate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.lastupdate") private String lastupdate; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.131+01:00", comments="Source field: exam.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.active") private Integer active; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.132+01:00", comments="Source field: exam.exam_template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.exam_template_id") private Long examTemplateId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.132+01:00", comments="Source field: exam.last_modified") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.361+02:00", comments="Source field: exam.last_modified") private Long lastModified; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.128+01:00", comments="Source Table: exam") - public ExamRecord(Long id, Long institutionId, Long lmsSetupId, String externalId, String owner, String supporter, String type, String quitPassword, String browserKeys, String status, Integer lmsSebRestriction, Integer updating, String lastupdate, Integer active, Long examTemplateId, Long lastModified) { + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.361+02:00", comments="Source field: exam.quiz_name") + private String quizName; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.361+02:00", comments="Source field: exam.quiz_start_time") + private DateTime quizStartTime; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.361+02:00", comments="Source field: exam.quiz_end_time") + private DateTime quizEndTime; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.361+02:00", comments="Source field: exam.lms_available") + private Integer lmsAvailable; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.359+02:00", comments="Source Table: exam") + public ExamRecord(Long id, Long institutionId, Long lmsSetupId, String externalId, String owner, String supporter, String type, String quitPassword, String browserKeys, String status, Integer lmsSebRestriction, Integer updating, String lastupdate, Integer active, Long examTemplateId, Long lastModified, String quizName, DateTime quizStartTime, DateTime quizEndTime, Integer lmsAvailable) { this.id = id; this.institutionId = institutionId; this.lmsSetupId = lmsSetupId; @@ -69,93 +82,117 @@ public class ExamRecord { this.active = active; this.examTemplateId = examTemplateId; this.lastModified = lastModified; + this.quizName = quizName; + this.quizStartTime = quizStartTime; + this.quizEndTime = quizEndTime; + this.lmsAvailable = lmsAvailable; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.359+02:00", comments="Source field: exam.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.359+02:00", comments="Source field: exam.institution_id") public Long getInstitutionId() { return institutionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.lms_setup_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.359+02:00", comments="Source field: exam.lms_setup_id") public Long getLmsSetupId() { return lmsSetupId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.external_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.359+02:00", comments="Source field: exam.external_id") public String getExternalId() { return externalId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.owner") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.359+02:00", comments="Source field: exam.owner") public String getOwner() { return owner; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.supporter") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.supporter") public String getSupporter() { return supporter; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.130+01:00", comments="Source field: exam.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.type") public String getType() { return type; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.131+01:00", comments="Source field: exam.quit_password") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.quit_password") public String getQuitPassword() { return quitPassword; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.131+01:00", comments="Source field: exam.browser_keys") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.browser_keys") public String getBrowserKeys() { return browserKeys; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.131+01:00", comments="Source field: exam.status") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.status") public String getStatus() { return status; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.131+01:00", comments="Source field: exam.lms_seb_restriction") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.lms_seb_restriction") public Integer getLmsSebRestriction() { return lmsSebRestriction; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.131+01:00", comments="Source field: exam.updating") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.updating") public Integer getUpdating() { return updating; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.131+01:00", comments="Source field: exam.lastupdate") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.lastupdate") public String getLastupdate() { return lastupdate; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.132+01:00", comments="Source field: exam.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.active") public Integer getActive() { return active; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.132+01:00", comments="Source field: exam.exam_template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.360+02:00", comments="Source field: exam.exam_template_id") public Long getExamTemplateId() { return examTemplateId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.132+01:00", comments="Source field: exam.last_modified") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.361+02:00", comments="Source field: exam.last_modified") public Long getLastModified() { return lastModified; } + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.361+02:00", comments="Source field: exam.quiz_name") + public String getQuizName() { + return quizName; + } + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.361+02:00", comments="Source field: exam.quiz_start_time") + public DateTime getQuizStartTime() { + return quizStartTime; + } + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.361+02:00", comments="Source field: exam.quiz_end_time") + public DateTime getQuizEndTime() { + return quizEndTime; + } + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.361+02:00", comments="Source field: exam.lms_available") + public Integer getLmsAvailable() { + return lmsAvailable; + } + /** * This method was generated by MyBatis Generator. * This method corresponds to the database table exam * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -179,6 +216,10 @@ public class ExamRecord { sb.append(", active=").append(active); sb.append(", examTemplateId=").append(examTemplateId); sb.append(", lastModified=").append(lastModified); + sb.append(", quizName=").append(quizName); + sb.append(", quizStartTime=").append(quizStartTime); + sb.append(", quizEndTime=").append(quizEndTime); + sb.append(", lmsAvailable=").append(lmsAvailable); sb.append("]"); return sb.toString(); } @@ -187,7 +228,7 @@ public class ExamRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table exam * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -216,14 +257,18 @@ public class ExamRecord { && (this.getLastupdate() == null ? other.getLastupdate() == null : this.getLastupdate().equals(other.getLastupdate())) && (this.getActive() == null ? other.getActive() == null : this.getActive().equals(other.getActive())) && (this.getExamTemplateId() == null ? other.getExamTemplateId() == null : this.getExamTemplateId().equals(other.getExamTemplateId())) - && (this.getLastModified() == null ? other.getLastModified() == null : this.getLastModified().equals(other.getLastModified())); + && (this.getLastModified() == null ? other.getLastModified() == null : this.getLastModified().equals(other.getLastModified())) + && (this.getQuizName() == null ? other.getQuizName() == null : this.getQuizName().equals(other.getQuizName())) + && (this.getQuizStartTime() == null ? other.getQuizStartTime() == null : this.getQuizStartTime().equals(other.getQuizStartTime())) + && (this.getQuizEndTime() == null ? other.getQuizEndTime() == null : this.getQuizEndTime().equals(other.getQuizEndTime())) + && (this.getLmsAvailable() == null ? other.getLmsAvailable() == null : this.getLmsAvailable().equals(other.getLmsAvailable())); } /** * This method was generated by MyBatis Generator. * This method corresponds to the database table exam * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { @@ -245,6 +290,10 @@ public class ExamRecord { result = prime * result + ((getActive() == null) ? 0 : getActive().hashCode()); result = prime * result + ((getExamTemplateId() == null) ? 0 : getExamTemplateId().hashCode()); result = prime * result + ((getLastModified() == null) ? 0 : getLastModified().hashCode()); + result = prime * result + ((getQuizName() == null) ? 0 : getQuizName().hashCode()); + result = prime * result + ((getQuizStartTime() == null) ? 0 : getQuizStartTime().hashCode()); + result = prime * result + ((getQuizEndTime() == null) ? 0 : getQuizEndTime().hashCode()); + result = prime * result + ((getLmsAvailable() == null) ? 0 : getLmsAvailable().hashCode()); return result; } } \ No newline at end of file diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ExamTemplateRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ExamTemplateRecord.java index e8dbd4eb..32657b39 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ExamTemplateRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ExamTemplateRecord.java @@ -3,34 +3,34 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class ExamTemplateRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.449+02:00", comments="Source field: exam_template.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.449+02:00", comments="Source field: exam_template.institution_id") private Long institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.configuration_template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.449+02:00", comments="Source field: exam_template.configuration_template_id") private Long configurationTemplateId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.449+02:00", comments="Source field: exam_template.name") private String name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.description") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.449+02:00", comments="Source field: exam_template.description") private String description; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.exam_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.449+02:00", comments="Source field: exam_template.exam_type") private String examType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.supporter") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.450+02:00", comments="Source field: exam_template.supporter") private String supporter; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.indicator_templates") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.450+02:00", comments="Source field: exam_template.indicator_templates") private String indicatorTemplates; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.institutional_default") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.451+02:00", comments="Source field: exam_template.institutional_default") private Integer institutionalDefault; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source Table: exam_template") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.449+02:00", comments="Source Table: exam_template") public ExamTemplateRecord(Long id, Long institutionId, Long configurationTemplateId, String name, String description, String examType, String supporter, String indicatorTemplates, Integer institutionalDefault) { this.id = id; this.institutionId = institutionId; @@ -43,47 +43,47 @@ public class ExamTemplateRecord { this.institutionalDefault = institutionalDefault; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.449+02:00", comments="Source field: exam_template.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.449+02:00", comments="Source field: exam_template.institution_id") public Long getInstitutionId() { return institutionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.configuration_template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.449+02:00", comments="Source field: exam_template.configuration_template_id") public Long getConfigurationTemplateId() { return configurationTemplateId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.449+02:00", comments="Source field: exam_template.name") public String getName() { return name; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.description") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.449+02:00", comments="Source field: exam_template.description") public String getDescription() { return description; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.exam_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.450+02:00", comments="Source field: exam_template.exam_type") public String getExamType() { return examType; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.supporter") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.450+02:00", comments="Source field: exam_template.supporter") public String getSupporter() { return supporter; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.indicator_templates") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.451+02:00", comments="Source field: exam_template.indicator_templates") public String getIndicatorTemplates() { return indicatorTemplates; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.215+01:00", comments="Source field: exam_template.institutional_default") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.451+02:00", comments="Source field: exam_template.institutional_default") public Integer getInstitutionalDefault() { return institutionalDefault; } @@ -92,7 +92,7 @@ public class ExamTemplateRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table exam_template * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -117,7 +117,7 @@ public class ExamTemplateRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table exam_template * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -146,7 +146,7 @@ public class ExamTemplateRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table exam_template * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/IndicatorRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/IndicatorRecord.java index f2f53b6a..e9288a4e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/IndicatorRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/IndicatorRecord.java @@ -3,28 +3,28 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class IndicatorRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source field: indicator.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source field: indicator.exam_id") private Long examId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source field: indicator.type") private String type; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source field: indicator.name") private String name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.color") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source field: indicator.color") private String color; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.icon") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source field: indicator.icon") private String icon; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.tags") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source field: indicator.tags") private String tags; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source Table: indicator") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source Table: indicator") public IndicatorRecord(Long id, Long examId, String type, String name, String color, String icon, String tags) { this.id = id; this.examId = examId; @@ -35,37 +35,37 @@ public class IndicatorRecord { this.tags = tags; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source field: indicator.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source field: indicator.exam_id") public Long getExamId() { return examId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source field: indicator.type") public String getType() { return type; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source field: indicator.name") public String getName() { return name; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.color") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source field: indicator.color") public String getColor() { return color; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.icon") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source field: indicator.icon") public String getIcon() { return icon; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.167+01:00", comments="Source field: indicator.tags") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.398+02:00", comments="Source field: indicator.tags") public String getTags() { return tags; } @@ -74,7 +74,7 @@ public class IndicatorRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table indicator * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -97,7 +97,7 @@ public class IndicatorRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table indicator * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -124,7 +124,7 @@ public class IndicatorRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table indicator * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/InstitutionRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/InstitutionRecord.java index ac36c89c..7660cd77 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/InstitutionRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/InstitutionRecord.java @@ -3,25 +3,25 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class InstitutionRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.173+01:00", comments="Source field: institution.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.412+02:00", comments="Source field: institution.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.173+01:00", comments="Source field: institution.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.412+02:00", comments="Source field: institution.name") private String name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.173+01:00", comments="Source field: institution.url_suffix") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.412+02:00", comments="Source field: institution.url_suffix") private String urlSuffix; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.174+01:00", comments="Source field: institution.theme_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.412+02:00", comments="Source field: institution.theme_name") private String themeName; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.174+01:00", comments="Source field: institution.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.413+02:00", comments="Source field: institution.active") private Integer active; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.174+01:00", comments="Source field: institution.logo_image") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.413+02:00", comments="Source field: institution.logo_image") private String logoImage; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.173+01:00", comments="Source Table: institution") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.412+02:00", comments="Source Table: institution") public InstitutionRecord(Long id, String name, String urlSuffix, String themeName, Integer active, String logoImage) { this.id = id; this.name = name; @@ -31,32 +31,32 @@ public class InstitutionRecord { this.logoImage = logoImage; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.173+01:00", comments="Source field: institution.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.412+02:00", comments="Source field: institution.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.173+01:00", comments="Source field: institution.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.412+02:00", comments="Source field: institution.name") public String getName() { return name; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.174+01:00", comments="Source field: institution.url_suffix") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.412+02:00", comments="Source field: institution.url_suffix") public String getUrlSuffix() { return urlSuffix; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.174+01:00", comments="Source field: institution.theme_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.412+02:00", comments="Source field: institution.theme_name") public String getThemeName() { return themeName; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.174+01:00", comments="Source field: institution.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.413+02:00", comments="Source field: institution.active") public Integer getActive() { return active; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.175+01:00", comments="Source field: institution.logo_image") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.413+02:00", comments="Source field: institution.logo_image") public String getLogoImage() { return logoImage; } @@ -65,7 +65,7 @@ public class InstitutionRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table institution * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -87,7 +87,7 @@ public class InstitutionRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table institution * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -113,7 +113,7 @@ public class InstitutionRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table institution * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/LmsSetupRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/LmsSetupRecord.java index a634c7f5..3207112a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/LmsSetupRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/LmsSetupRecord.java @@ -3,49 +3,49 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class LmsSetupRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.institution_id") private Long institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.name") private String name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_type") private String lmsType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_url") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_url") private String lmsUrl; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_clientname") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_clientname") private String lmsClientname; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_clientsecret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_clientsecret") private String lmsClientsecret; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_rest_api_token") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_rest_api_token") private String lmsRestApiToken; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_proxy_host") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_proxy_host") private String lmsProxyHost; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_proxy_port") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_proxy_port") private Integer lmsProxyPort; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_proxy_auth_username") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_proxy_auth_username") private String lmsProxyAuthUsername; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_proxy_auth_secret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_proxy_auth_secret") private String lmsProxyAuthSecret; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.update_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.update_time") private Long updateTime; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.active") private Integer active; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source Table: lms_setup") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source Table: lms_setup") public LmsSetupRecord(Long id, Long institutionId, String name, String lmsType, String lmsUrl, String lmsClientname, String lmsClientsecret, String lmsRestApiToken, String lmsProxyHost, Integer lmsProxyPort, String lmsProxyAuthUsername, String lmsProxyAuthSecret, Long updateTime, Integer active) { this.id = id; this.institutionId = institutionId; @@ -63,72 +63,72 @@ public class LmsSetupRecord { this.active = active; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.institution_id") public Long getInstitutionId() { return institutionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.name") public String getName() { return name; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_type") public String getLmsType() { return lmsType; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_url") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_url") public String getLmsUrl() { return lmsUrl; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_clientname") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_clientname") public String getLmsClientname() { return lmsClientname; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_clientsecret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_clientsecret") public String getLmsClientsecret() { return lmsClientsecret; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_rest_api_token") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_rest_api_token") public String getLmsRestApiToken() { return lmsRestApiToken; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_proxy_host") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_proxy_host") public String getLmsProxyHost() { return lmsProxyHost; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_proxy_port") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_proxy_port") public Integer getLmsProxyPort() { return lmsProxyPort; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_proxy_auth_username") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_proxy_auth_username") public String getLmsProxyAuthUsername() { return lmsProxyAuthUsername; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.lms_proxy_auth_secret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.lms_proxy_auth_secret") public String getLmsProxyAuthSecret() { return lmsProxyAuthSecret; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.update_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.421+02:00", comments="Source field: lms_setup.update_time") public Long getUpdateTime() { return updateTime; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.182+01:00", comments="Source field: lms_setup.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.422+02:00", comments="Source field: lms_setup.active") public Integer getActive() { return active; } @@ -137,7 +137,7 @@ public class LmsSetupRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table lms_setup * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -167,7 +167,7 @@ public class LmsSetupRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table lms_setup * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -201,7 +201,7 @@ public class LmsSetupRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table lms_setup * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/OrientationRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/OrientationRecord.java index 414a453f..ff0bafb2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/OrientationRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/OrientationRecord.java @@ -3,37 +3,37 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class OrientationRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.103+01:00", comments="Source field: orientation.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.330+02:00", comments="Source field: orientation.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.103+01:00", comments="Source field: orientation.config_attribute_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.330+02:00", comments="Source field: orientation.config_attribute_id") private Long configAttributeId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.103+01:00", comments="Source field: orientation.template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.330+02:00", comments="Source field: orientation.template_id") private Long templateId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.view_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.330+02:00", comments="Source field: orientation.view_id") private Long viewId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.group_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.331+02:00", comments="Source field: orientation.group_id") private String groupId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.x_position") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.331+02:00", comments="Source field: orientation.x_position") private Integer xPosition; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.y_position") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.331+02:00", comments="Source field: orientation.y_position") private Integer yPosition; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.width") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.331+02:00", comments="Source field: orientation.width") private Integer width; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.height") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.331+02:00", comments="Source field: orientation.height") private Integer height; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.title") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.331+02:00", comments="Source field: orientation.title") private String title; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.103+01:00", comments="Source Table: orientation") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.330+02:00", comments="Source Table: orientation") public OrientationRecord(Long id, Long configAttributeId, Long templateId, Long viewId, String groupId, Integer xPosition, Integer yPosition, Integer width, Integer height, String title) { this.id = id; this.configAttributeId = configAttributeId; @@ -47,52 +47,52 @@ public class OrientationRecord { this.title = title; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.103+01:00", comments="Source field: orientation.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.330+02:00", comments="Source field: orientation.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.103+01:00", comments="Source field: orientation.config_attribute_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.330+02:00", comments="Source field: orientation.config_attribute_id") public Long getConfigAttributeId() { return configAttributeId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.330+02:00", comments="Source field: orientation.template_id") public Long getTemplateId() { return templateId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.view_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.330+02:00", comments="Source field: orientation.view_id") public Long getViewId() { return viewId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.group_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.331+02:00", comments="Source field: orientation.group_id") public String getGroupId() { return groupId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.x_position") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.331+02:00", comments="Source field: orientation.x_position") public Integer getxPosition() { return xPosition; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.y_position") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.331+02:00", comments="Source field: orientation.y_position") public Integer getyPosition() { return yPosition; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.width") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.331+02:00", comments="Source field: orientation.width") public Integer getWidth() { return width; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.height") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.331+02:00", comments="Source field: orientation.height") public Integer getHeight() { return height; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.104+01:00", comments="Source field: orientation.title") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.332+02:00", comments="Source field: orientation.title") public String getTitle() { return title; } @@ -101,7 +101,7 @@ public class OrientationRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table orientation * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -127,7 +127,7 @@ public class OrientationRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table orientation * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -157,7 +157,7 @@ public class OrientationRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table orientation * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/RemoteProctoringRoomRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/RemoteProctoringRoomRecord.java index f490dc7a..fc1e17db 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/RemoteProctoringRoomRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/RemoteProctoringRoomRecord.java @@ -3,34 +3,34 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class RemoteProctoringRoomRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.378+02:00", comments="Source field: remote_proctoring_room.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.378+02:00", comments="Source field: remote_proctoring_room.exam_id") private Long examId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.378+02:00", comments="Source field: remote_proctoring_room.name") private String name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.size") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.378+02:00", comments="Source field: remote_proctoring_room.size") private Integer size; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.subject") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.379+02:00", comments="Source field: remote_proctoring_room.subject") private String subject; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.townhall_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.379+02:00", comments="Source field: remote_proctoring_room.townhall_room") private Integer townhallRoom; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.break_out_connections") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.379+02:00", comments="Source field: remote_proctoring_room.break_out_connections") private String breakOutConnections; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.join_key") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.379+02:00", comments="Source field: remote_proctoring_room.join_key") private String joinKey; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.145+01:00", comments="Source field: remote_proctoring_room.room_data") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.379+02:00", comments="Source field: remote_proctoring_room.room_data") private String roomData; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source Table: remote_proctoring_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.378+02:00", comments="Source Table: remote_proctoring_room") public RemoteProctoringRoomRecord(Long id, Long examId, String name, Integer size, String subject, Integer townhallRoom, String breakOutConnections, String joinKey, String roomData) { this.id = id; this.examId = examId; @@ -43,47 +43,47 @@ public class RemoteProctoringRoomRecord { this.roomData = roomData; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.378+02:00", comments="Source field: remote_proctoring_room.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.exam_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.378+02:00", comments="Source field: remote_proctoring_room.exam_id") public Long getExamId() { return examId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.378+02:00", comments="Source field: remote_proctoring_room.name") public String getName() { return name; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.size") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.378+02:00", comments="Source field: remote_proctoring_room.size") public Integer getSize() { return size; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.subject") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.379+02:00", comments="Source field: remote_proctoring_room.subject") public String getSubject() { return subject; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.townhall_room") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.379+02:00", comments="Source field: remote_proctoring_room.townhall_room") public Integer getTownhallRoom() { return townhallRoom; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.break_out_connections") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.379+02:00", comments="Source field: remote_proctoring_room.break_out_connections") public String getBreakOutConnections() { return breakOutConnections; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.144+01:00", comments="Source field: remote_proctoring_room.join_key") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.379+02:00", comments="Source field: remote_proctoring_room.join_key") public String getJoinKey() { return joinKey; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.145+01:00", comments="Source field: remote_proctoring_room.room_data") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.379+02:00", comments="Source field: remote_proctoring_room.room_data") public String getRoomData() { return roomData; } @@ -92,7 +92,7 @@ public class RemoteProctoringRoomRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table remote_proctoring_room * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -117,7 +117,7 @@ public class RemoteProctoringRoomRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table remote_proctoring_room * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -146,7 +146,7 @@ public class RemoteProctoringRoomRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table remote_proctoring_room * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/RoleRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/RoleRecord.java index 46f3bc21..69d731c6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/RoleRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/RoleRecord.java @@ -3,33 +3,33 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class RoleRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.196+01:00", comments="Source field: user_role.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.429+02:00", comments="Source field: user_role.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.196+01:00", comments="Source field: user_role.user_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.429+02:00", comments="Source field: user_role.user_id") private Long userId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.197+01:00", comments="Source field: user_role.role_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.429+02:00", comments="Source field: user_role.role_name") private String roleName; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.196+01:00", comments="Source Table: user_role") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.429+02:00", comments="Source Table: user_role") public RoleRecord(Long id, Long userId, String roleName) { this.id = id; this.userId = userId; this.roleName = roleName; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.196+01:00", comments="Source field: user_role.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.429+02:00", comments="Source field: user_role.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.196+01:00", comments="Source field: user_role.user_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.429+02:00", comments="Source field: user_role.user_id") public Long getUserId() { return userId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.197+01:00", comments="Source field: user_role.role_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.429+02:00", comments="Source field: user_role.role_name") public String getRoleName() { return roleName; } @@ -38,7 +38,7 @@ public class RoleRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table user_role * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -57,7 +57,7 @@ public class RoleRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table user_role * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -80,7 +80,7 @@ public class RoleRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table user_role * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/SebClientConfigRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/SebClientConfigRecord.java index 67e657b8..c5076050 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/SebClientConfigRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/SebClientConfigRecord.java @@ -4,32 +4,38 @@ import javax.annotation.Generated; import org.joda.time.DateTime; public class SebClientConfigRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.417+02:00", comments="Source field: seb_client_configuration.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.417+02:00", comments="Source field: seb_client_configuration.institution_id") private Long institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.417+02:00", comments="Source field: seb_client_configuration.name") private String name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.date") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.417+02:00", comments="Source field: seb_client_configuration.date") private DateTime date; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.client_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source field: seb_client_configuration.client_name") private String clientName; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.client_secret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source field: seb_client_configuration.client_secret") private String clientSecret; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.encrypt_secret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source field: seb_client_configuration.encrypt_secret") private String encryptSecret; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source field: seb_client_configuration.active") private Integer active; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source Table: seb_client_configuration") - public SebClientConfigRecord(Long id, Long institutionId, String name, DateTime date, String clientName, String clientSecret, String encryptSecret, Integer active) { + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source field: seb_client_configuration.last_update_time") + private Long lastUpdateTime; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source field: seb_client_configuration.last_update_user") + private String lastUpdateUser; + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.415+02:00", comments="Source Table: seb_client_configuration") + public SebClientConfigRecord(Long id, Long institutionId, String name, DateTime date, String clientName, String clientSecret, String encryptSecret, Integer active, Long lastUpdateTime, String lastUpdateUser) { this.id = id; this.institutionId = institutionId; this.name = name; @@ -38,53 +44,65 @@ public class SebClientConfigRecord { this.clientSecret = clientSecret; this.encryptSecret = encryptSecret; this.active = active; + this.lastUpdateTime = lastUpdateTime; + this.lastUpdateUser = lastUpdateUser; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.417+02:00", comments="Source field: seb_client_configuration.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.417+02:00", comments="Source field: seb_client_configuration.institution_id") public Long getInstitutionId() { return institutionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.417+02:00", comments="Source field: seb_client_configuration.name") public String getName() { return name; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.date") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source field: seb_client_configuration.date") public DateTime getDate() { return date; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.client_name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source field: seb_client_configuration.client_name") public String getClientName() { return clientName; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.client_secret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source field: seb_client_configuration.client_secret") public String getClientSecret() { return clientSecret; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.encrypt_secret") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source field: seb_client_configuration.encrypt_secret") public String getEncryptSecret() { return encryptSecret; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.177+01:00", comments="Source field: seb_client_configuration.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source field: seb_client_configuration.active") public Integer getActive() { return active; } + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source field: seb_client_configuration.last_update_time") + public Long getLastUpdateTime() { + return lastUpdateTime; + } + + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.418+02:00", comments="Source field: seb_client_configuration.last_update_user") + public String getLastUpdateUser() { + return lastUpdateUser; + } + /** * This method was generated by MyBatis Generator. * This method corresponds to the database table seb_client_configuration * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -100,6 +118,8 @@ public class SebClientConfigRecord { sb.append(", clientSecret=").append(clientSecret); sb.append(", encryptSecret=").append(encryptSecret); sb.append(", active=").append(active); + sb.append(", lastUpdateTime=").append(lastUpdateTime); + sb.append(", lastUpdateUser=").append(lastUpdateUser); sb.append("]"); return sb.toString(); } @@ -108,7 +128,7 @@ public class SebClientConfigRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table seb_client_configuration * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -129,14 +149,16 @@ public class SebClientConfigRecord { && (this.getClientName() == null ? other.getClientName() == null : this.getClientName().equals(other.getClientName())) && (this.getClientSecret() == null ? other.getClientSecret() == null : this.getClientSecret().equals(other.getClientSecret())) && (this.getEncryptSecret() == null ? other.getEncryptSecret() == null : this.getEncryptSecret().equals(other.getEncryptSecret())) - && (this.getActive() == null ? other.getActive() == null : this.getActive().equals(other.getActive())); + && (this.getActive() == null ? other.getActive() == null : this.getActive().equals(other.getActive())) + && (this.getLastUpdateTime() == null ? other.getLastUpdateTime() == null : this.getLastUpdateTime().equals(other.getLastUpdateTime())) + && (this.getLastUpdateUser() == null ? other.getLastUpdateUser() == null : this.getLastUpdateUser().equals(other.getLastUpdateUser())); } /** * This method was generated by MyBatis Generator. * This method corresponds to the database table seb_client_configuration * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { @@ -150,6 +172,8 @@ public class SebClientConfigRecord { result = prime * result + ((getClientSecret() == null) ? 0 : getClientSecret().hashCode()); result = prime * result + ((getEncryptSecret() == null) ? 0 : getEncryptSecret().hashCode()); result = prime * result + ((getActive() == null) ? 0 : getActive().hashCode()); + result = prime * result + ((getLastUpdateTime() == null) ? 0 : getLastUpdateTime().hashCode()); + result = prime * result + ((getLastUpdateUser() == null) ? 0 : getLastUpdateUser().hashCode()); return result; } } \ No newline at end of file diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ThresholdRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ThresholdRecord.java index ba330324..8377cfe8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ThresholdRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ThresholdRecord.java @@ -4,22 +4,22 @@ import java.math.BigDecimal; import javax.annotation.Generated; public class ThresholdRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.169+01:00", comments="Source field: threshold.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.404+02:00", comments="Source field: threshold.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.169+01:00", comments="Source field: threshold.indicator_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.404+02:00", comments="Source field: threshold.indicator_id") private Long indicatorId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.170+01:00", comments="Source field: threshold.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.404+02:00", comments="Source field: threshold.value") private BigDecimal value; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.170+01:00", comments="Source field: threshold.color") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.404+02:00", comments="Source field: threshold.color") private String color; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.170+01:00", comments="Source field: threshold.icon") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.404+02:00", comments="Source field: threshold.icon") private String icon; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.169+01:00", comments="Source Table: threshold") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.404+02:00", comments="Source Table: threshold") public ThresholdRecord(Long id, Long indicatorId, BigDecimal value, String color, String icon) { this.id = id; this.indicatorId = indicatorId; @@ -28,27 +28,27 @@ public class ThresholdRecord { this.icon = icon; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.169+01:00", comments="Source field: threshold.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.404+02:00", comments="Source field: threshold.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.170+01:00", comments="Source field: threshold.indicator_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.404+02:00", comments="Source field: threshold.indicator_id") public Long getIndicatorId() { return indicatorId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.170+01:00", comments="Source field: threshold.value") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.404+02:00", comments="Source field: threshold.value") public BigDecimal getValue() { return value; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.170+01:00", comments="Source field: threshold.color") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.404+02:00", comments="Source field: threshold.color") public String getColor() { return color; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.170+01:00", comments="Source field: threshold.icon") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.404+02:00", comments="Source field: threshold.icon") public String getIcon() { return icon; } @@ -57,7 +57,7 @@ public class ThresholdRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table threshold * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -78,7 +78,7 @@ public class ThresholdRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table threshold * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -103,7 +103,7 @@ public class ThresholdRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table threshold * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/UserActivityLogRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/UserActivityLogRecord.java index 5a3421cb..b63bee99 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/UserActivityLogRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/UserActivityLogRecord.java @@ -3,28 +3,28 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class UserActivityLogRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source field: user_activity_log.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.436+02:00", comments="Source field: user_activity_log.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source field: user_activity_log.user_uuid") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.436+02:00", comments="Source field: user_activity_log.user_uuid") private String userUuid; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source field: user_activity_log.timestamp") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.436+02:00", comments="Source field: user_activity_log.timestamp") private Long timestamp; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.200+01:00", comments="Source field: user_activity_log.activity_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.436+02:00", comments="Source field: user_activity_log.activity_type") private String activityType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.200+01:00", comments="Source field: user_activity_log.entity_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.436+02:00", comments="Source field: user_activity_log.entity_type") private String entityType; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.200+01:00", comments="Source field: user_activity_log.entity_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.436+02:00", comments="Source field: user_activity_log.entity_id") private String entityId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.200+01:00", comments="Source field: user_activity_log.message") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.436+02:00", comments="Source field: user_activity_log.message") private String message; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source Table: user_activity_log") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.435+02:00", comments="Source Table: user_activity_log") public UserActivityLogRecord(Long id, String userUuid, Long timestamp, String activityType, String entityType, String entityId, String message) { this.id = id; this.userUuid = userUuid; @@ -35,37 +35,37 @@ public class UserActivityLogRecord { this.message = message; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source field: user_activity_log.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.436+02:00", comments="Source field: user_activity_log.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source field: user_activity_log.user_uuid") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.436+02:00", comments="Source field: user_activity_log.user_uuid") public String getUserUuid() { return userUuid; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.199+01:00", comments="Source field: user_activity_log.timestamp") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.436+02:00", comments="Source field: user_activity_log.timestamp") public Long getTimestamp() { return timestamp; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.200+01:00", comments="Source field: user_activity_log.activity_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.436+02:00", comments="Source field: user_activity_log.activity_type") public String getActivityType() { return activityType; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.200+01:00", comments="Source field: user_activity_log.entity_type") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.436+02:00", comments="Source field: user_activity_log.entity_type") public String getEntityType() { return entityType; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.200+01:00", comments="Source field: user_activity_log.entity_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.436+02:00", comments="Source field: user_activity_log.entity_id") public String getEntityId() { return entityId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.200+01:00", comments="Source field: user_activity_log.message") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.436+02:00", comments="Source field: user_activity_log.message") public String getMessage() { return message; } @@ -74,7 +74,7 @@ public class UserActivityLogRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table user_activity_log * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -97,7 +97,7 @@ public class UserActivityLogRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table user_activity_log * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -124,7 +124,7 @@ public class UserActivityLogRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table user_activity_log * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/UserRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/UserRecord.java index 97a28d69..e61538dd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/UserRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/UserRecord.java @@ -4,43 +4,43 @@ import javax.annotation.Generated; import org.joda.time.DateTime; public class UserRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.191+01:00", comments="Source field: user.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.191+01:00", comments="Source field: user.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.institution_id") private Long institutionId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.191+01:00", comments="Source field: user.uuid") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.uuid") private String uuid; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.191+01:00", comments="Source field: user.creation_date") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.creation_date") private DateTime creationDate; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.191+01:00", comments="Source field: user.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.name") private String name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.192+01:00", comments="Source field: user.surname") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.surname") private String surname; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.192+01:00", comments="Source field: user.username") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.username") private String username; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.192+01:00", comments="Source field: user.password") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.password") private String password; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.192+01:00", comments="Source field: user.email") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.email") private String email; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.192+01:00", comments="Source field: user.language") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.language") private String language; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.192+01:00", comments="Source field: user.timezone") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.426+02:00", comments="Source field: user.timezone") private String timezone; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.192+01:00", comments="Source field: user.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.426+02:00", comments="Source field: user.active") private Integer active; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.191+01:00", comments="Source Table: user") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source Table: user") public UserRecord(Long id, Long institutionId, String uuid, DateTime creationDate, String name, String surname, String username, String password, String email, String language, String timezone, Integer active) { this.id = id; this.institutionId = institutionId; @@ -56,62 +56,62 @@ public class UserRecord { this.active = active; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.191+01:00", comments="Source field: user.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.191+01:00", comments="Source field: user.institution_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.institution_id") public Long getInstitutionId() { return institutionId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.191+01:00", comments="Source field: user.uuid") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.uuid") public String getUuid() { return uuid; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.191+01:00", comments="Source field: user.creation_date") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.creation_date") public DateTime getCreationDate() { return creationDate; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.191+01:00", comments="Source field: user.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.name") public String getName() { return name; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.192+01:00", comments="Source field: user.surname") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.surname") public String getSurname() { return surname; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.192+01:00", comments="Source field: user.username") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.username") public String getUsername() { return username; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.192+01:00", comments="Source field: user.password") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.password") public String getPassword() { return password; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.192+01:00", comments="Source field: user.email") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.425+02:00", comments="Source field: user.email") public String getEmail() { return email; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.192+01:00", comments="Source field: user.language") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.426+02:00", comments="Source field: user.language") public String getLanguage() { return language; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.192+01:00", comments="Source field: user.timezone") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.426+02:00", comments="Source field: user.timezone") public String getTimezone() { return timezone; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.192+01:00", comments="Source field: user.active") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.426+02:00", comments="Source field: user.active") public Integer getActive() { return active; } @@ -120,7 +120,7 @@ public class UserRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table user * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -148,7 +148,7 @@ public class UserRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table user * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -180,7 +180,7 @@ public class UserRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table user * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ViewRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ViewRecord.java index 9cacb2a4..ca793067 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ViewRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/ViewRecord.java @@ -3,22 +3,22 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class ViewRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.099+01:00", comments="Source field: view.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.324+02:00", comments="Source field: view.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.099+01:00", comments="Source field: view.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.324+02:00", comments="Source field: view.name") private String name; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.099+01:00", comments="Source field: view.columns") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.324+02:00", comments="Source field: view.columns") private Integer columns; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.099+01:00", comments="Source field: view.position") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.325+02:00", comments="Source field: view.position") private Integer position; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.099+01:00", comments="Source field: view.template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.325+02:00", comments="Source field: view.template_id") private Long templateId; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.099+01:00", comments="Source Table: view") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.324+02:00", comments="Source Table: view") public ViewRecord(Long id, String name, Integer columns, Integer position, Long templateId) { this.id = id; this.name = name; @@ -27,27 +27,27 @@ public class ViewRecord { this.templateId = templateId; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.099+01:00", comments="Source field: view.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.324+02:00", comments="Source field: view.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.099+01:00", comments="Source field: view.name") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.324+02:00", comments="Source field: view.name") public String getName() { return name; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.099+01:00", comments="Source field: view.columns") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.325+02:00", comments="Source field: view.columns") public Integer getColumns() { return columns; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.099+01:00", comments="Source field: view.position") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.325+02:00", comments="Source field: view.position") public Integer getPosition() { return position; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.099+01:00", comments="Source field: view.template_id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.325+02:00", comments="Source field: view.template_id") public Long getTemplateId() { return templateId; } @@ -56,7 +56,7 @@ public class ViewRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table view * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -77,7 +77,7 @@ public class ViewRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table view * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -102,7 +102,7 @@ public class ViewRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table view * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/WebserviceServerInfoRecord.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/WebserviceServerInfoRecord.java index 499be1d7..72f37546 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/WebserviceServerInfoRecord.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/batis/model/WebserviceServerInfoRecord.java @@ -3,22 +3,22 @@ package ch.ethz.seb.sebserver.webservice.datalayer.batis.model; import javax.annotation.Generated; public class WebserviceServerInfoRecord { - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source field: webservice_server_info.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.442+02:00", comments="Source field: webservice_server_info.id") private Long id; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source field: webservice_server_info.uuid") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.442+02:00", comments="Source field: webservice_server_info.uuid") private String uuid; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source field: webservice_server_info.service_address") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.443+02:00", comments="Source field: webservice_server_info.service_address") private String serviceAddress; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source field: webservice_server_info.master") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.443+02:00", comments="Source field: webservice_server_info.master") private Integer master; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source field: webservice_server_info.update_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.443+02:00", comments="Source field: webservice_server_info.update_time") private Long updateTime; - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source Table: webservice_server_info") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.442+02:00", comments="Source Table: webservice_server_info") public WebserviceServerInfoRecord(Long id, String uuid, String serviceAddress, Integer master, Long updateTime) { this.id = id; this.uuid = uuid; @@ -27,27 +27,27 @@ public class WebserviceServerInfoRecord { this.updateTime = updateTime; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source field: webservice_server_info.id") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.442+02:00", comments="Source field: webservice_server_info.id") public Long getId() { return id; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source field: webservice_server_info.uuid") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.443+02:00", comments="Source field: webservice_server_info.uuid") public String getUuid() { return uuid; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source field: webservice_server_info.service_address") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.443+02:00", comments="Source field: webservice_server_info.service_address") public String getServiceAddress() { return serviceAddress; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source field: webservice_server_info.master") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.443+02:00", comments="Source field: webservice_server_info.master") public Integer getMaster() { return master; } - @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-01-18T17:36:21.207+01:00", comments="Source field: webservice_server_info.update_time") + @Generated(value="org.mybatis.generator.api.MyBatisGenerator", date="2022-05-16T11:24:18.443+02:00", comments="Source field: webservice_server_info.update_time") public Long getUpdateTime() { return updateTime; } @@ -56,7 +56,7 @@ public class WebserviceServerInfoRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table webservice_server_info * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public String toString() { @@ -77,7 +77,7 @@ public class WebserviceServerInfoRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table webservice_server_info * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public boolean equals(Object that) { @@ -102,7 +102,7 @@ public class WebserviceServerInfoRecord { * This method was generated by MyBatis Generator. * This method corresponds to the database table webservice_server_info * - * @mbg.generated Tue Jan 18 17:36:21 CET 2022 + * @mbg.generated Mon May 16 11:24:18 CEST 2022 */ @Override public int hashCode() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/OrientationTableDuplicatesCheck.java b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/OrientationTableDuplicatesCheck.java index ad1a226f..b5935d6d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/OrientationTableDuplicatesCheck.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/datalayer/checks/OrientationTableDuplicatesCheck.java @@ -66,22 +66,23 @@ public class OrientationTableDuplicatesCheck implements DBIntegrityCheck { } } - if (toDelete.isEmpty()) { + final List checkedToDelete = toDelete + .stream() + .filter(this::doubleCheck) + .collect(Collectors.toList()); + + if (checkedToDelete == null || checkedToDelete.isEmpty()) { return "OK"; } if (tryFix) { - final List checkedToDelete = toDelete - .stream() - .filter(this::doubleCheck) - .collect(Collectors.toList()); checkedToDelete .stream() .forEach(this.orientationRecordMapper::deleteByPrimaryKey); - return "Fixed duplicates by deletion: " + checkedToDelete + " from findings:" + toDelete; + return "Fixed duplicates by deletion: " + checkedToDelete; } else { - return "Found duplicates: " + toDelete; + return "Found duplicates: " + checkedToDelete; } }); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationService.java index f35d938c..eb4d8e71 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationService.java @@ -19,7 +19,7 @@ import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.util.Result; -/** A service to apply pagination functionality within collection results form data access layer. +/** A service to apply pagination functionality within collection results from data access layer. * The default implementation uses Mybatis-PageHelper to apply the pagination on SQL level where possible: * https://github.com/pagehelper/Mybatis-PageHelper */ @@ -108,24 +108,33 @@ public interface PaginationService { * @param pageSize the size of a page * @param sort the page sort flag * @param all list of all entities, unsorted - * @param sorter a sorter function that sorts the list for specific type of entries + * @param pageFunction a function that filter and sorts the list for specific type of entries * @return current page of objects from the sorted list of entities */ default Page buildPageFromList( final Integer pageNumber, final Integer pageSize, final String sort, final Collection all, - final Function, List> sorter) { + final Function, List> pageFunction) { - final List sorted = sorter.apply(all); - final int _pageNumber = getPageNumber(pageNumber); + final List sorted = pageFunction.apply(all); + + int _pageNumber = getPageNumber(pageNumber); final int _pageSize = getPageSize(pageSize); - final int start = (_pageNumber - 1) * _pageSize; + + int start = (_pageNumber - 1) * _pageSize; + if (start >= sorted.size()) { + start = 0; + _pageNumber = 1; + } int end = start + _pageSize; if (sorted.size() < end) { end = sorted.size(); } - final int numberOfPages = sorted.size() / _pageSize; + int numberOfPages = sorted.size() / _pageSize; + if (sorted.size() % _pageSize > 0) { + numberOfPages++; + } return new Page<>( (numberOfPages > 0) ? numberOfPages : 1, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationServiceImpl.java index 090e3cac..1503ec37 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationServiceImpl.java @@ -282,10 +282,11 @@ public class PaginationServiceImpl implements PaginationService { final Map examTableMap = new HashMap<>(); examTableMap.put(Entity.FILTER_ATTR_INSTITUTION, institutionNameRef); examTableMap.put(Domain.EXAM.ATTR_LMS_SETUP_ID, lmsSetupNameRef); - - // NOTE: This seems not to work and I was not able to figure out why. - // Now the type sorting is done within secondary sort for exams. - //examTableMap.put(Domain.EXAM.ATTR_TYPE, "'" + ExamRecordDynamicSqlSupport.type.name() + "'"); + examTableMap.put(Domain.EXAM.ATTR_QUIZ_NAME, ExamRecordDynamicSqlSupport.quizName.name()); + examTableMap.put(Domain.EXAM.ATTR_QUIZ_START_TIME, ExamRecordDynamicSqlSupport.quizStartTime.name()); + examTableMap.put(Domain.EXAM.ATTR_QUIZ_END_TIME, ExamRecordDynamicSqlSupport.quizEndTime.name()); + examTableMap.put(Domain.EXAM.ATTR_STATUS, ExamRecordDynamicSqlSupport.status.name()); + examTableMap.put(Domain.EXAM.ATTR_TYPE, ExamRecordDynamicSqlSupport.type.name()); this.sortColumnMapping.put(ExamRecordDynamicSqlSupport.examRecord.name(), examTableMap); this.defaultSortColumn.put(ExamRecordDynamicSqlSupport.examRecord.name(), Domain.EXAM.ATTR_ID); @@ -326,6 +327,10 @@ public class PaginationServiceImpl implements PaginationService { configurationNodeTableMap.put( Domain.CONFIGURATION_NODE.ATTR_STATUS, ConfigurationNodeRecordDynamicSqlSupport.status.name()); + configurationNodeTableMap.put( + Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID, + ConfigurationNodeRecordDynamicSqlSupport.templateId.name()); + this.sortColumnMapping.put( ConfigurationNodeRecordDynamicSqlSupport.configurationNodeRecord.name(), configurationNodeTableMap); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationService.java index 509fd0dc..992a80c2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationService.java @@ -194,6 +194,47 @@ public interface AuthorizationService { } + /** Check grant by using corresponding hasGrant(XY) method and throws PermissionDeniedException + * on deny. + * + * @param privilegeType the privilege type to check + * @param userInfo the the user + * @param grantEntity the entity */ + default T check( + final PrivilegeType privilegeType, + final UserInfo userInfo, + final T grantEntity) { + + // check institutional grant + if (hasGrant( + PrivilegeType.MODIFY, + EntityType.CONFIGURATION_NODE, + grantEntity.getInstitutionId(), + userInfo.uuid, + userInfo.uuid, + userInfo.institutionId, + userInfo.getUserRoles())) { + return grantEntity; + } + + // if there is no institutional grant the user may have owner based grant on the specified realm + // TODO +// return userInfo.getUserRoles() +// .stream() +// .map(role -> new RoleTypeKey(entityType, role)) +// .map(this.privileges::get) +// .anyMatch(privilege -> (privilege != null) && privilege.hasOwnershipPrivilege(privilegeType)); +// if (hasOwnerPrivilege(privilegeType, entityType, institutionId)) { +// return; +// } + + throw new PermissionDeniedException( + grantEntity.entityType(), + privilegeType, + getUserService().getCurrentUser().getUserInfo()); + + } + /** Indicates if the current user has an owner privilege for this give entity type and institution * * @param privilegeType the privilege type to check @@ -223,7 +264,7 @@ public interface AuthorizationService { * on deny or returns the given grantEntity within a Result on successful grant. * This is useful to use with a Result based functional chain. * - * @param entity The entity instance to check overall read access */ + * @param entity The entity instance to check overall read access */ default Result checkRead(final E entity) { return check(PrivilegeType.READ, entity); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserService.java index a5557dae..14ad2f4d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserService.java @@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.authorization; import java.security.Principal; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.WebDataBinder; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser; @@ -51,4 +52,9 @@ public interface UserService { * @param binder Springs WebDataBinder is injected on controller side */ void addUsersInstitutionDefaultPropertySupport(final WebDataBinder binder); + /** Used to set authentication on different thread. + * + * @param authentication */ + void setAuthenticationIfAbsent(Authentication authentication); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/AuthorizationServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/AuthorizationServiceImpl.java index 782beec5..d4d40e1c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/AuthorizationServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/AuthorizationServiceImpl.java @@ -129,7 +129,7 @@ public class AuthorizationServiceImpl implements AuthorizationService { .forRole(UserRole.SEB_SERVER_ADMIN) .withBasePrivilege(PrivilegeType.READ) .andForRole(UserRole.INSTITUTIONAL_ADMIN) - .withInstitutionalPrivilege(PrivilegeType.READ) + .withInstitutionalPrivilege(PrivilegeType.WRITE) .andForRole(UserRole.EXAM_ADMIN) .withInstitutionalPrivilege(PrivilegeType.WRITE) .create(); @@ -145,6 +145,7 @@ public class AuthorizationServiceImpl implements AuthorizationService { .andForRole(UserRole.EXAM_SUPPORTER) .withInstitutionalPrivilege(PrivilegeType.READ) .create(); + // grants for configuration addPrivilege(EntityType.CONFIGURATION) .forRole(UserRole.SEB_SERVER_ADMIN) @@ -156,6 +157,7 @@ public class AuthorizationServiceImpl implements AuthorizationService { .andForRole(UserRole.EXAM_SUPPORTER) .withInstitutionalPrivilege(PrivilegeType.READ) .create(); + // grants for configuration value addPrivilege(EntityType.CONFIGURATION_VALUE) .forRole(UserRole.SEB_SERVER_ADMIN) @@ -219,6 +221,17 @@ public class AuthorizationServiceImpl implements AuthorizationService { .andForRole(UserRole.INSTITUTIONAL_ADMIN) .withInstitutionalPrivilege(PrivilegeType.READ) .create(); + + // grants for batch actions + addPrivilege(EntityType.BATCH_ACTION) + .forRole(UserRole.SEB_SERVER_ADMIN) + .withBasePrivilege(PrivilegeType.READ) + .withInstitutionalPrivilege(PrivilegeType.WRITE) + .andForRole(UserRole.INSTITUTIONAL_ADMIN) + .withInstitutionalPrivilege(PrivilegeType.WRITE) + .andForRole(UserRole.EXAM_ADMIN) + .withInstitutionalPrivilege(PrivilegeType.WRITE) + .create(); } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/SEBServerUser.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/SEBServerUser.java index eb1c0595..03e3f0ea 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/SEBServerUser.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/SEBServerUser.java @@ -13,6 +13,7 @@ import java.util.Collections; import java.util.EnumSet; import java.util.stream.Collectors; +import org.springframework.security.core.Authentication; import org.springframework.security.core.CredentialsContainer; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -26,7 +27,7 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserRole; * * This implements Spring's UserDetails and CredentialsContainer to act as a principal * within internal authentication and authorization processes. */ -public final class SEBServerUser implements UserDetails, CredentialsContainer { +public final class SEBServerUser implements UserDetails, CredentialsContainer, Authentication { private static final long serialVersionUID = 5726250141482925769L; @@ -160,4 +161,34 @@ public final class SEBServerUser implements UserDetails, CredentialsContainer { return new SEBServerUser(user.id, UserInfo.of(user.userInfo), user.password); } + @Override + public String getName() { + return this.userInfo.username; + } + + @Override + public Object getCredentials() { + return this; + } + + @Override + public Object getDetails() { + return this; + } + + @Override + public Object getPrincipal() { + return this; + } + + @Override + public boolean isAuthenticated() { + return isEnabled(); + } + + @Override + public void setAuthenticated(final boolean isAuthenticated) throws IllegalArgumentException { + + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/UserServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/UserServiceImpl.java index 4204ea3a..19cfd637 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/UserServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/UserServiceImpl.java @@ -94,6 +94,13 @@ public class UserServiceImpl implements UserService { binder.registerCustomEditor(Long.class, usersInstitutionDefaultEditor); } + @Override + public void setAuthenticationIfAbsent(final Authentication authentication) { + if (SecurityContextHolder.getContext().getAuthentication() == null) { + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } + // 1. OAuth2Authentication strategy @Lazy @Component @@ -115,6 +122,21 @@ public class UserServiceImpl implements UserService { } } + // 2. Separated thread strategy + @Lazy + @Component + public static class OtherThreadUserExtractStrategy implements ExtractUserFromAuthenticationStrategy { + + @Override + public SEBServerUser extract(final Principal principal) { + if (principal instanceof SEBServerUser) { + return (SEBServerUser) principal; + } + + return null; + } + } + private static final SEBServerUser ANONYMOUS_USER = new SEBServerUser( -1L, new UserInfo("SEB_SERVER_ANONYMOUS_USER", -2L, null, "anonymous", "anonymous", "anonymous", null, false, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BatchActionExec.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BatchActionExec.java index de1273c8..81ebb4c4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BatchActionExec.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BatchActionExec.java @@ -8,7 +8,11 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction; +import java.util.Map; + import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.model.BatchAction; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -21,10 +25,19 @@ public interface BatchActionExec { * @return action type of the batch action */ BatchActionType actionType(); + /** Implements a consistency check for the given batch action attributes. + * This shall check whether the needed attributes are available for a proper processing of the actions. + * This is called just before a new batch action is created and processing is started. + * + * @param batchAction + * @return APIMessage if there is an consistency failure */ + APIMessage checkConsistency(Map actionAttributes); + /** Executes the action on a single entity. * * @param modelId The model identifier of the entity to process + * @param batchAction The batch action metadata * @return Result refer to the entity key or to an error when happened */ - Result doSingleAction(String modelId); + Result doSingleAction(String modelId, BatchAction batchAction); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BatchActionService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BatchActionService.java index 0c35fedb..26b1ddf7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BatchActionService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BatchActionService.java @@ -10,7 +10,6 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction; import java.util.Collection; -import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.BatchAction; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -20,13 +19,22 @@ import ch.ethz.seb.sebserver.gbl.util.Result; * can be reported to the user showing successful action as well as actions that has failed. */ public interface BatchActionService { - /** Use this to register a new batch action for further processing. + /** Validates a given batch action. * - * @param institutionId The institution identifier - * @param actionType The batch action type - * @param ids comma separated String of model ids to process - * @return Result refer to the stored batch action or to an error when happened */ - Result registerNewBatchAction(final Long institutionId, BatchActionType actionType, String ids); + * @param batchAction + * @return Result refer to the BatchAction or to an error when happened */ + Result validate(BatchAction batchAction); + + /** Use this to notify a new batch action for further processing. + * + * @param batchAction BatchAction */ + Result notifyNewBatchAction(BatchAction batchAction); + + /** Use this to get a specific BatchAction. + * + * @param actionId The batch action identifier + * @return Result refer to the batch actions or to an error when happened */ + Result getRunningAction(String actionId); /** Use this to get all currently running batch actions for a given institution. * diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java index 57e063fa..6787f2cb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java @@ -56,7 +56,7 @@ public interface BulkActionService { * If the given BulkAction has not already been executed, it will be executed first * * @param action the BulkAction of a concrete type - * @return EntityProcessingReport extracted form an executed BulkAction */ + * @return EntityProcessingReport extracted from an executed BulkAction */ Result createReport(BulkAction action); } \ No newline at end of file diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupportDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupportDAO.java index 0d66dad2..cd01814b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupportDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionSupportDAO.java @@ -25,7 +25,6 @@ import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ActivatableEntityDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DAOLoggingSupport; -import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO; /** Defines overall DAO support for bulk-actions like activate, deactivate, delete... * @@ -71,17 +70,24 @@ public interface BulkActionSupportDAO { .get(error -> handleBulkActionError(error, all)) : Collections.emptyList(); case HARD_DELETE: - return (this instanceof EntityDAO) - ? ((EntityDAO) this).delete(all) - .map(BulkActionSupportDAO::transformResult) - .get(error -> handleBulkActionError(error, all)) - : Collections.emptyList(); + return delete(all) + .map(BulkActionSupportDAO::transformResult) + .get(error -> handleBulkActionError(error, all)); } // should never happen throw new UnsupportedOperationException("Unsupported Bulk Action: " + bulkAction); } + /** Use this to delete all entities defined by a set of EntityKey + * NOTE: the Set of EntityKey may contain EntityKey of other entity types like the concrete type of the DAO + * use extractPKsFromKeys to get a list of concrete primary keys for entities to delete + * + * @param all The Collection of EntityKey to delete + * @return Result referring a collection of all entities that has been deleted or refer to an error if + * happened */ + Result> delete(Set all); + /** This creates a collection of Results refer the given entity keys. * * @param keys Collection of entity keys to create Results from diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BatchActionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BatchActionServiceImpl.java index 9940f57e..42be44b6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BatchActionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BatchActionServiceImpl.java @@ -9,23 +9,27 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl; import java.time.Instant; -import java.util.Arrays; import java.util.Collection; import java.util.EnumMap; import java.util.HashSet; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ScheduledFuture; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; -import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.BatchAction; import ch.ethz.seb.sebserver.gbl.model.EntityKey; @@ -36,6 +40,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BatchActionServi import ch.ethz.seb.sebserver.webservice.servicelayer.dao.BatchActionDAO; 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.UserActivityLogDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO; @Service @WebServiceProfile @@ -44,48 +50,65 @@ public class BatchActionServiceImpl implements BatchActionService { private static final Logger log = LoggerFactory.getLogger(BatchActionServiceImpl.class); private final BatchActionDAO batchActionDAO; + private final UserDAO userDAO; private final TaskScheduler taskScheduler; + private final UserActivityLogDAO userActivityLogDAO; private final EnumMap batchExecutions; + private ScheduledFuture runningBatchProcess = null; + public BatchActionServiceImpl( final BatchActionDAO batchActionDAO, + final UserDAO userDAO, + final UserActivityLogDAO userActivityLogDAO, final Collection batchExecutions, final TaskScheduler taskScheduler) { this.batchActionDAO = batchActionDAO; + this.userDAO = userDAO; + this.userActivityLogDAO = userActivityLogDAO; this.taskScheduler = taskScheduler; this.batchExecutions = new EnumMap<>(BatchActionType.class); - batchExecutions.stream() - .map(exec -> this.batchExecutions.putIfAbsent(exec.actionType(), exec)) - .findAny() - .ifPresent(exec -> log.error( - "BatchActionExec mismatch. It seems there is already a BatchActionExec for type: {} registered!", - exec.actionType())); + batchExecutions + .stream() + .forEach(exec -> this.batchExecutions.putIfAbsent(exec.actionType(), exec)); } @Override - public Result registerNewBatchAction( - final Long institutionId, - final BatchActionType actionType, - final String ids) { + public Result validate(final BatchAction batchAction) { return Result.tryCatch(() -> { - final Collection sourceIds = Arrays.asList(StringUtils.split( - ids, - Constants.LIST_SEPARATOR)); + final BatchActionExec batchActionExec = this.batchExecutions.get(batchAction.actionType); + if (batchActionExec == null) { + throw new IllegalArgumentException( + "Batch action execution not found for batch action type: " + batchAction.actionType); + } + + final APIMessage consistencyError = batchActionExec.checkConsistency(batchAction.attributes); + if (consistencyError != null) { + throw new APIMessageException(consistencyError); + } + + return batchAction; - return this.batchActionDAO - .createNew(new BatchAction(null, institutionId, actionType, sourceIds, null, null, null)) - .map(res -> { - processNextBatchAction(); - return res; - }) - .getOrThrow(); }); } + @Override + public Result notifyNewBatchAction(final BatchAction batchAction) { + return Result.tryCatch(() -> { + processNextBatchAction(); + return batchAction; + }); + } + + @Override + public Result getRunningAction(final String actionId) { + return this.batchActionDAO.byModelId(actionId); + } + @Override public Result> getRunningActions(final Long institutionId) { return this.batchActionDAO.allMatching(new FilterMap().putIfAbsent( @@ -93,7 +116,7 @@ public class BatchActionServiceImpl implements BatchActionService { String.valueOf(institutionId))) .map(results -> results.stream() .filter(action -> StringUtils.isNotBlank(action.processorId) && - !action.processorId.endsWith(BatchActionDAO.FLAG_FINISHED)) + !action.processorId.endsWith(BatchAction.FINISHED_FLAG)) .collect(Collectors.toList())); } @@ -104,7 +127,7 @@ public class BatchActionServiceImpl implements BatchActionService { String.valueOf(institutionId))) .map(results -> results.stream() .filter(action -> StringUtils.isNotBlank(action.processorId) && - !action.processorId.endsWith(BatchActionDAO.FLAG_FINISHED)) + !action.processorId.endsWith(BatchAction.FINISHED_FLAG)) .filter(action -> action.actionType.entityType == entityType) .collect(Collectors.toList())); } @@ -116,89 +139,187 @@ public class BatchActionServiceImpl implements BatchActionService { String.valueOf(institutionId))) .map(results -> results.stream() .filter(action -> StringUtils.isNotBlank(action.processorId) && - action.processorId.endsWith(BatchActionDAO.FLAG_FINISHED)) + action.processorId.endsWith(BatchAction.FINISHED_FLAG)) .collect(Collectors.toList())); } + @Scheduled( + fixedDelayString = "${sebserver.webservice.batchaction.update-interval:60000}", + initialDelay = 60000) + private void processing() { + processNextBatchAction(); + } + private void processNextBatchAction() { + + if (this.runningBatchProcess != null && !this.runningBatchProcess.isDone()) { + return; + } + try { - this.taskScheduler.schedule( - new BatchActionProcess(this.batchActionDAO, this.batchExecutions), - Instant.now()); + final String processorId = UUID.randomUUID().toString(); + log.debug("Check for pending batch action with processorId: {}", processorId); + + this.batchActionDAO + .getAndReserveNext(processorId) + .onSuccess(action -> { + this.runningBatchProcess = this.taskScheduler.schedule( + new BatchActionProcess( + new BatchActionHandlerImpl(action), + this.batchExecutions.get(action.actionType), + action, + getAuthentication(action)), + Instant.now()); + }) + .onError(error -> { + if (error instanceof ResourceNotFoundException) { + log.debug("No pending batch action found..."); + } else { + throw new RuntimeException(error); + } + }); } catch (final Exception e) { log.error("Failed to schedule BatchActionProcess task: ", e); } } + private Authentication getAuthentication(final BatchAction action) { + try { + final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null) { + return authentication; + } + + return this.userDAO.byModelId(action.ownerId) + .flatMap(userInfo -> this.userDAO.sebServerUserByUsername(userInfo.username)) + .onError(error -> log.error("Failed to get batch action owner user -> ", error)) + .getOr(null); + + } catch (final Exception e) { + log.error("Failed to get authentication: ", e); + return null; + } + } + private final static class BatchActionProcess implements Runnable { - private final BatchActionDAO batchActionDAO; - private final EnumMap batchExecutions; - private final String processorId = UUID.randomUUID().toString(); + private final BatchActionHandler batchActionHandler; + private final BatchActionExec batchActionExec; + private final BatchAction batchAction; + private final Authentication authentication; - private BatchAction batchAction; private Set processingIds; - private Set failedIds; public BatchActionProcess( - final BatchActionDAO batchActionDAO, - final EnumMap batchExecutions) { + final BatchActionHandler batchActionHandler, + final BatchActionExec batchActionExec, + final BatchAction batchAction, + final Authentication authentication) { - this.batchActionDAO = batchActionDAO; - this.batchExecutions = batchExecutions; + this.batchActionHandler = batchActionHandler; + this.batchActionExec = batchActionExec; + this.batchAction = batchAction; + this.authentication = authentication; } @Override public void run() { try { - this.batchAction = this.batchActionDAO.getAndReserveNext(this.processorId) - .onErrorDo(error -> { - if (error instanceof ResourceNotFoundException) { - log.info("No batch pending actions found for processing."); - return null; - } else { - throw new RuntimeException(error); - } - }) - .getOrThrow(); + log.info("Starting or continuing batch action - {}", this.batchAction); - if (this.batchAction == null) { - return; + if (SecurityContextHolder.getContext().getAuthentication() == null) { + if (this.authentication == null) { + throw new IllegalStateException("No authentication found within batch context"); + } + SecurityContextHolder.getContext().setAuthentication(this.authentication); } - final BatchActionExec batchActionExec = this.batchExecutions.get(this.batchAction.actionType); - this.processingIds = new HashSet<>(this.batchAction.sourceIds); this.processingIds.removeAll(this.batchAction.successful); - this.failedIds = new HashSet<>(); this.processingIds .stream() .forEach(modelId -> { - final Result doSingleAction = batchActionExec.doSingleAction(modelId); - if (doSingleAction.hasError()) { - log.error( - "Failed to process single entity on batch action. ModelId: {}, action: ", - modelId, - this.batchAction, - doSingleAction.getError()); - this.failedIds.add(modelId); - } else { - this.batchActionDAO.updateProgress(null, modelId, this.failedIds); + if (log.isDebugEnabled()) { + log.debug("Process batch action type: {}, id: {}", + this.batchAction.actionType, + modelId); } + + this.batchActionExec + .doSingleAction(modelId, this.batchAction) + .onError(error -> this.batchActionHandler.handleError(modelId, error)) + .onSuccess(entityKey -> this.batchActionHandler.handleSuccess(entityKey)); }); + this.batchActionHandler.finishUp(); + } catch (final Exception e) { log.error("Unexpected error while batch action processing. processorId: {} action: ", - this.processorId, + this.batchAction.processorId, this.batchAction); - log.info("Skip this batch action."); + log.info("Skip this batch action... new batch action process will be started automatically"); } } } + private interface BatchActionHandler { + + void handleSuccess(final EntityKey entityKey); + + void handleError(final String modelId, final Exception error); + + void finishUp(); + } + + private final class BatchActionHandlerImpl implements BatchActionHandler { + + public final BatchAction batchAction; + + public BatchActionHandlerImpl(final BatchAction batchAction) { + this.batchAction = batchAction; + } + + @Override + public void handleSuccess(final EntityKey entityKey) { + BatchActionServiceImpl.this.batchActionDAO.setSuccessfull( + this.batchAction.id, + this.batchAction.processorId, + entityKey.modelId); + } + + @Override + public void handleError(final String modelId, final Exception error) { + log.error( + "Failed to process single entity on batch action. ModelId: {}, action: {}, errorMessage: {}", + modelId, + this.batchAction, + error.getMessage()); + + BatchActionServiceImpl.this.batchActionDAO.setFailure( + this.batchAction.id, + this.batchAction.processorId, + modelId, + error); + } + + @Override + public void finishUp() { + BatchActionServiceImpl.this.batchActionDAO + .finishUp(this.batchAction.id, this.batchAction.processorId, false) + .onSuccess(action -> log.info("Finished batch action - {}", action)) + .onError(error -> log.error( + "Failed to mark batch action as finished: {}", + this.batchAction, error)); + + BatchActionServiceImpl.this.batchActionDAO.byPK(this.batchAction.id) + .flatMap(BatchActionServiceImpl.this.userActivityLogDAO::logFinished) + .onError(error -> log.error("Failed to put audit log for batch action finish: ", error)); + } + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkActionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkActionServiceImpl.java index b165d44c..b782532b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkActionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkActionServiceImpl.java @@ -255,13 +255,16 @@ public class BulkActionServiceImpl implements BulkActionService { case INSTITUTION: return Arrays.asList( this.supporter.get(EntityType.LMS_SETUP), + this.supporter.get(EntityType.CERTIFICATE), + this.supporter.get(EntityType.BATCH_ACTION), this.supporter.get(EntityType.USER), this.supporter.get(EntityType.EXAM), this.supporter.get(EntityType.INDICATOR), this.supporter.get(EntityType.SEB_CLIENT_CONFIGURATION), - this.supporter.get(EntityType.EXAM_CONFIGURATION_MAP), this.supporter.get(EntityType.CLIENT_CONNECTION), - this.supporter.get(EntityType.CONFIGURATION_NODE)); + this.supporter.get(EntityType.CONFIGURATION_NODE), + this.supporter.get(EntityType.EXAM_CONFIGURATION_MAP), + this.supporter.get(EntityType.EXAM_TEMPLATE)); case USER: return Arrays.asList( this.supporter.get(EntityType.EXAM), diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/ExamConfigResetToTemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/ExamConfigResetToTemplate.java new file mode 100644 index 00000000..cbbe1477 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/ExamConfigResetToTemplate.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl; + +import java.util.Map; + +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType; +import ch.ethz.seb.sebserver.gbl.model.BatchAction; +import ch.ethz.seb.sebserver.gbl.model.Entity; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; +import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BatchActionExec; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ExamConfigService; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamConfigUpdateService; + +@Lazy +@Component +@WebServiceProfile +public class ExamConfigResetToTemplate implements BatchActionExec { + + private final ConfigurationNodeDAO configurationNodeDAO; + private final ExamConfigService sebExamConfigService; + private final ExamConfigUpdateService examConfigUpdateService; + private final AuthorizationService authorizationService; + + public ExamConfigResetToTemplate( + final ConfigurationNodeDAO configurationNodeDAO, + final ExamConfigService sebExamConfigService, + final ExamConfigUpdateService examConfigUpdateService, + final AuthorizationService authorizationService) { + + this.configurationNodeDAO = configurationNodeDAO; + this.sebExamConfigService = sebExamConfigService; + this.examConfigUpdateService = examConfigUpdateService; + this.authorizationService = authorizationService; + } + + @Override + public BatchActionType actionType() { + return BatchActionType.EXAM_CONFIG_REST_TEMPLATE_SETTINGS; + } + + @Override + public APIMessage checkConsistency(final Map actionAttributes) { + // no additional check here + return null; + } + + @Override + public Result doSingleAction(final String modelId, final BatchAction batchAction) { + + return this.configurationNodeDAO + .byModelId(modelId) + .flatMap(node -> this.authorizationService.check(PrivilegeType.MODIFY, node)) + .map(this::checkConsistency) + .flatMap(this.sebExamConfigService::resetToTemplateSettings) + .map(node -> { + this.examConfigUpdateService + .processExamConfigurationChange(node.id) + .getOrThrow(); + return node; + }) + .map(Entity::getEntityKey); + + } + + private ConfigurationNode checkConsistency(final ConfigurationNode configurationNode) { + return configurationNode; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/ExamConfigStateChange.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/ExamConfigStateChange.java new file mode 100644 index 00000000..489a8841 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/ExamConfigStateChange.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl; + +import java.util.Map; + +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType; +import ch.ethz.seb.sebserver.gbl.model.BatchAction; +import ch.ethz.seb.sebserver.gbl.model.Entity; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +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.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BatchActionExec; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ExamConfigService; +import io.micrometer.core.instrument.util.StringUtils; + +@Lazy +@Component +@WebServiceProfile +public class ExamConfigStateChange implements BatchActionExec { + + private final ExamConfigService sebExamConfigService; + private final ConfigurationNodeDAO configurationNodeDAO; + private final AuthorizationService authorizationService; + + public ExamConfigStateChange( + final ExamConfigService sebExamConfigService, + final ConfigurationNodeDAO configurationNodeDAO, + final AuthorizationService authorizationService) { + + this.sebExamConfigService = sebExamConfigService; + this.configurationNodeDAO = configurationNodeDAO; + this.authorizationService = authorizationService; + } + + @Override + public BatchActionType actionType() { + return BatchActionType.EXAM_CONFIG_STATE_CHANGE; + } + + @Override + public APIMessage checkConsistency(final Map actionAttributes) { + final ConfigurationStatus targetState = getTargetState(actionAttributes); + if (targetState == null) { + APIMessage.ErrorMessage.ILLEGAL_API_ARGUMENT + .of("Missing target state attribute for EXAM_CONFIG_STATE_CHANGE batch action"); + } + return null; + } + + @Override + public Result doSingleAction(final String modelId, final BatchAction batchAction) { + + return this.configurationNodeDAO + .byModelId(modelId) + .flatMap(node -> this.authorizationService.check(PrivilegeType.MODIFY, node)) + .map(node -> new ConfigurationNode( + node.id, null, null, null, null, null, null, + getTargetState(batchAction.attributes), + Utils.toDateTimeUTC(Utils.getMillisecondsNow()), + batchAction.ownerId)) + .flatMap(this.sebExamConfigService::checkSaveConsistency) + .flatMap(this.configurationNodeDAO::save) + .map(Entity::getEntityKey); + + } + + private ConfigurationStatus getTargetState(final Map actionAttributes) { + try { + final String targetStateString = actionAttributes.get(BatchAction.ACTION_ATTRIBUT_TARGET_STATE); + if (StringUtils.isBlank(targetStateString)) { + return null; + } + + return ConfigurationStatus.valueOf(targetStateString); + } catch (final Exception e) { + return null; + } + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ActivatableEntityDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ActivatableEntityDAO.java index 8e2cde4d..b03dadda 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ActivatableEntityDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ActivatableEntityDAO.java @@ -42,7 +42,7 @@ public interface ActivatableEntityDAO Result> setActive(Set all, boolean active); default Result setActive(final T entity, final boolean active) { - return setActive(new HashSet<>(Arrays.asList(entity.getEntityKey())), true) + return setActive(new HashSet<>(Arrays.asList(entity.getEntityKey())), active) .flatMap(result -> byModelId(result.iterator().next().modelId)); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/AdditionalAttributesDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/AdditionalAttributesDAO.java index 1d73b34b..ed41f2e5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/AdditionalAttributesDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/AdditionalAttributesDAO.java @@ -13,6 +13,7 @@ import java.util.Map; import java.util.stream.Collectors; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord; @@ -22,6 +23,18 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttribut * in a separated data-base table. */ public interface AdditionalAttributesDAO { + /** Use this to get all additional attribute records for a specific entity. + * + * @param type the entity type + * @param entityId the entity identifier (primary key) + * @return Result refer to the collection of additional attribute records or to an error if happened */ + default Result> getAdditionalAttributes(final EntityKey entityKey) { + return Result.tryCatch(() -> getAdditionalAttributes( + entityKey.entityType, + Long.valueOf(entityKey.modelId)) + .getOrThrow()); + } + /** Use this to get all additional attribute records for a specific entity. * * @param type the entity type diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/BatchActionDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/BatchActionDAO.java index a36bd531..79783268 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/BatchActionDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/BatchActionDAO.java @@ -8,14 +8,11 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao; -import java.util.Collection; - import ch.ethz.seb.sebserver.gbl.model.BatchAction; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO; -public interface BatchActionDAO extends EntityDAO { - - public static final String FLAG_FINISHED = "_FINISHED"; +public interface BatchActionDAO extends EntityDAO, BulkActionSupportDAO { /** This checks if there is a pending batch action to process next. * If so this reserves the pending batch action and mark it to be processed @@ -26,14 +23,6 @@ public interface BatchActionDAO extends EntityDAO { * @return Result refer to the batch action to process or to an error when happened */ Result getAndReserveNext(String processId); - /** Use this to update the processing of a running batch action - * - * @param actionId The batch action identifier - * @param processId The process identifier (must match with the processId on persistent storage) - * @param modelIds Collection of model identifiers of entities that has successfully been processed. - * @return Result refer to the involved batch action or to an error when happened. */ - Result updateProgress(Long actionId, String processId, Collection modelIds); - /** Use this to mark processing of a single entity of a specified batch action as successful completed. * * @param actionId The batch action identifier @@ -41,6 +30,14 @@ public interface BatchActionDAO extends EntityDAO { * @param modelId The model identifier to mark as completed for the given batch action */ void setSuccessfull(Long actionId, String processId, String modelId); + /** Use this to mark processing of a single entity of a specified batch action as failed. + * + * @param actionId The batch action identifier + * @param processId The process identifier (must match with the processId on persistent storage) + * @param modelId The model identifier to mark as failure for the given batch action + * @param error The failure error if available */ + void setFailure(Long actionId, String processId, String modelId, Exception error); + /** This is used by a processing background task that is processing a batch action to finish up * its work and register the batch action as done within the persistent storage. *

diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/CertificateDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/CertificateDAO.java index d6cde6b2..964c08e4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/CertificateDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/CertificateDAO.java @@ -17,9 +17,10 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.sebconfig.CertificateInfo; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Certificates; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO; /** Concrete EntityDAO interface of Certificate entities */ -public interface CertificateDAO { +public interface CertificateDAO extends BulkActionSupportDAO { Result getCertificate(final Long institutionId, String alias); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java index 3dcd593c..611a8d30 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java @@ -11,10 +11,11 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao; import java.util.Collection; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -26,6 +27,8 @@ public interface ClientConnectionDAO extends EntityDAO, BulkActionSupportDAO { + Logger log = LoggerFactory.getLogger(ClientConnectionDAO.class); + String CONNECTION_TOKENS_CACHE = "CONNECTION_TOKENS_CACHE"; /** Get a list of all connection tokens of all connections (no matter what state) @@ -43,27 +46,28 @@ public interface ClientConnectionDAO extends unless = "#result.hasError()") Result> getConnectionTokens(Long examId); - @CacheEvict(cacheNames = CONNECTION_TOKENS_CACHE, key = "#examId") + @CacheEvict( + cacheNames = CONNECTION_TOKENS_CACHE, + key = "#examId") default void evictConnectionTokenCache(final Long examId) { - - } - - /** Get a list of all connection tokens of all connections (no matter what state) - * of an exam. - * - * @param examId The exam identifier - * @return list of all connection tokens of all connections (no matter what state) - * of an exam */ - default Result> getConnectionTokensNoCache(final Long examId) { - return getConnectionTokens(examId); + if (log.isDebugEnabled()) { + log.debug("Evict SEB connection tokens for exam: {}", examId); + } } /** Get a list of all connection tokens of all connections of an exam - * that are in state active + * that are in state ConnectionStatus.ACTIVE * * @param examId The exam identifier * @return Result refer to the collection of connection tokens or to an error when happened */ - Result> getActiveConnctionTokens(Long examId); + Result> getActiveConnectionTokens(Long examId); + + /** Get a list of all connection tokens of all connections of an exam + * that are in state an active state. See ClientConnection + * + * @param examId The exam identifier + * @return Result refer to the collection of connection tokens or to an error when happened */ + Result> getAllActiveConnectionTokens(Long examId); /** Get all inactive connection tokens from the set of given tokens. * This is usually used for cleanup purposes to filter a bunch of connection tokens @@ -104,22 +108,6 @@ public interface ClientConnectionDAO extends * @return Result refer to a collection of all ClientConnection of the room or to an error if happened */ Result> getCollectingRoomConnections(final Long examId, final String roomName); - /** Creates new ClientConnection from the given ClientConnection data. - * - * This evicts all entries from the CONNECTION_TOKENS_CACHE. - * - * TODO improvement: Use the examId to evict only the relevant cache entry - * - * @param data ClientConnection instance - * @return Result refer to the newly created ClientConnection data or to an error if happened */ - @Override - @CacheEvict(cacheNames = CONNECTION_TOKENS_CACHE, allEntries = true) - Result createNew(ClientConnection data); - - @Override - @CacheEvict(cacheNames = CONNECTION_TOKENS_CACHE, allEntries = true) - Result save(ClientConnection data); - @CacheEvict( cacheNames = ExamSessionCacheService.CACHE_NAME_ACTIVE_CLIENT_CONNECTION, key = "#connectionToken") @@ -133,18 +121,6 @@ public interface ClientConnectionDAO extends /** Used to re-mark a client connection record for room update in error case. */ Result markForProctoringUpdate(Long id); - /** Deletes the given ClientConnection data. - * - * This evicts all entries from the CONNECTION_TOKENS_CACHE. - * - * TODO improvement: Use the examId to evict only the relevant cache entry - * - * @param all Set of EntityKey for entities to delete - * @return Result refer to a collection of deleted entities or to an error if happened */ - @Override - @CacheEvict(cacheNames = CONNECTION_TOKENS_CACHE, allEntries = true) - Result> delete(Set all); - /** Get a ClientConnection by connection token. * * @param connectionToken the connection token diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientInstructionDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientInstructionDAO.java index 795a801f..c18c4e53 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientInstructionDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientInstructionDAO.java @@ -45,7 +45,7 @@ public interface ClientInstructionDAO { * @return Collection of all active instructions for specified connection token */ Result> getAllActive(String connectionToken); - /** Deletes all old instructions form the persistent storage to clean-up. + /** Deletes all old instructions from the persistent storage to clean-up. * Old in this case means the timestamp is older then one minute or a configured time interval * * @param timestamp the time-stamp (milliseconds) of the time in the past from that earlier instructions are @@ -53,7 +53,7 @@ public interface ClientInstructionDAO { * @return Result collection of keys of deleted entities or refer to an error when happened */ Result> deleteAllInactive(long timestamp); - /** Deletes the specified instruction form the data base + /** Deletes the specified instruction from the data base * * @param id the identifier (PK) if the ClientInstruction to delete * @return Void Result refer to an error if happened */ diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ConfigurationDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ConfigurationDAO.java index 51c1449e..a04e67a4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ConfigurationDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ConfigurationDAO.java @@ -38,7 +38,7 @@ public interface ConfigurationDAO extends EntityDAO saveToHistory(Long configurationNodeId); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/DAOUserServcie.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/DAOUserServcie.java new file mode 100644 index 00000000..ce761afa --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/DAOUserServcie.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.dao; + +public interface DAOUserServcie { + + String getCurrentUserUUID(); + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java index d213eb17..b1b57af5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java @@ -148,14 +148,14 @@ public interface EntityDAO { * @return Result referring to collection of all matching entities or an error if happened */ Result> allMatching(FilterMap filterMap, Predicate predicate); - /** Context based utility method to extract an expected single resource entry form a Collection of specified type. - * Gets a Result refer to an expected single resource entry form a Collection of specified type or refer + /** Context based utility method to extract an expected single resource entry from a Collection of specified type. + * Gets a Result refer to an expected single resource entry from a Collection of specified type or refer * to a ResourceNotFoundException if specified collection is null or empty or refer to a * unexpected RuntimeException if there are more then the expected single element in the given collection * * @param id The resource id to wrap within a ResourceNotFoundException if needed * @param resources the collection of resource entries - * @return Result refer to an expected single resource entry form a Collection of specified type or refer to an + * @return Result refer to an expected single resource entry from a Collection of specified type or refer to an * error if happened */ default Result getSingleResource(final String id, final Collection resources) { @@ -180,22 +180,7 @@ public interface EntityDAO { * @param keys Collection of EntityKey of various types * @return Set of id's (PK's) from the given key collection that match the concrete EntityType */ default Set extractPKsFromKeys(final Collection keys) { - try { - - if (keys == null) { - return Collections.emptySet(); - } - - final EntityType entityType = entityType(); - return keys - .stream() - .filter(key -> key.entityType == entityType) - .map(key -> Long.valueOf(key.modelId)) - .collect(Collectors.toSet()); - } catch (final Exception e) { - log.error("unexpected error while trying to extract PK's from EntityKey's : ", e); - return Collections.emptySet(); - } + return extractPKsFromKeys(keys, entityType()); } /** Context based utility method to extract a set of id's (PK) from a collection of various EntityKey @@ -211,4 +196,32 @@ public interface EntityDAO { return new ArrayList<>(extractPKsFromKeys(keys)); } + /** Context based utility method to extract a set of id's (PK) from a collection of various EntityKey + * This uses the EntityType defined by this instance to filter all EntityKey by the given type and + * convert the matching EntityKey's to id's (PK's) + * + * Use this if you need to transform a Collection of EntityKey into a extracted Set of id's of a specified + * EntityType + * + * @param keys Collection of EntityKey of various types + * @param entityType the entity type of the keys to extract + * @return Set of id's (PK's) from the given key collection that match the concrete EntityType */ + static Set extractPKsFromKeys(final Collection keys, final EntityType entityType) { + try { + + if (keys == null) { + return Collections.emptySet(); + } + + return keys + .stream() + .filter(key -> key.entityType == entityType) + .map(key -> Long.valueOf(key.modelId)) + .collect(Collectors.toSet()); + } catch (final Exception e) { + log.error("unexpected error while trying to extract PK's from EntityKey's : ", e); + return Collections.emptySet(); + } + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamDAO.java index c1c4cdba..429a8ddc 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamDAO.java @@ -9,6 +9,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao; import java.util.Collection; +import java.util.function.Predicate; import org.springframework.cache.annotation.CacheEvict; @@ -16,6 +17,7 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.GrantEntity; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; +import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ExamSessionCacheService; @@ -23,12 +25,6 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ExamSessionCac /** Concrete EntityDAO interface of Exam entities */ public interface ExamDAO extends ActivatableEntityDAO, BulkActionSupportDAO { - /** Loads the specified exam with all data and additional attributes. - * - * @param examId the exam identifier to load - * @return Result refer to the loaded exam or to an error when happened */ - Result loadWithAdditionalAttributes(Long examId); - /** Get a GrantEntity for the exam of specified id (PK) * This is actually a Exam instance but with no course data loaded. * @@ -85,22 +81,30 @@ public interface ExamDAO extends ActivatableEntityDAO, BulkActionSup /** Use this to get identifiers of all exams in a specified state for a specified institution. * - * @param institutionId the institution identifier. May be null for all institutions - * @param status the ExamStatus + * @param filterMap FilterMap with other filter criteria + * @param status the list of ExamStatus * @return Result refer to collection of exam identifiers or to an error if happened */ - Result> getExamIdsForStatus(Long institutionId, ExamStatus status); + Result> getExamsForStatus( + final FilterMap filterMap, + final Predicate predicate, + final ExamStatus... status); - /** This is used to get all Exams to check if they have to set into running state in the meanwhile. - * Gets all exams in the upcoming status for run-check + /** Gets all for active and none archived exams within the system, independently from institution and LMSSetup. * - * @return Result refer to a collection of exams or to an error if happened */ - Result> allForRunCheck(); + * @return Result refer to all exams for LMS update or to an error when happened */ + Result> allForLMSUpdate(); - /** This is used to get all Exams to check if they have to set into finished state in the meanwhile. - * Gets all exams in the running status for end-check + /** This is used to get all Exams that potentially needs a state change. + * Checks if the stored running time frame of the exam is not in sync with the current state and return + * all exams for this is the case. + * Adding also leadTime before and followupTime after the specified running time frame of the exam for + * this check. * + * @param leadTime Time period in milliseconds that is added to now-time-point to check the start time of the exam + * @param followupTime Time period in milliseconds that is subtracted from now-time-point check the end time of the + * exam * @return Result refer to a collection of exams or to an error if happened */ - Result> allForEndCheck(); + Result> allThatNeedsStatusUpdate(long leadTime, long followupTime); /** Get a collection of all currently running exam identifiers * @@ -185,4 +189,24 @@ public interface ExamDAO extends ActivatableEntityDAO, BulkActionSup * @return Result refer to the collection of entity keys of all involved exams or to an error when happened */ Result> deleteTemplateReferences(Long examTemplateId); + /** This is used by the internal update process to update the quiz data for the specified exam. + * This shall only be called if there are changes to the quiz data of the exam since this also + * refreshes the running exam cache. + * + * @param examId the exam identifier + * @param quizData The quiz data to update + * @param updateId The update identifier given by the update task + * @return Result refer to the given QuizData or to an error when happened */ + @CacheEvict( + cacheNames = ExamSessionCacheService.CACHE_NAME_RUNNING_EXAM, + key = "#examId") + Result updateQuizData(Long examId, QuizData quizData, String updateId); + + /** This is used by the internal update process to mark exams for which the LMS related data availability + * + * @param externalQuizId The exams external UUID or quiz id of the exam to mark + * @param available The LMS availability flag to set + * @param updateId The update identifier given by the update task */ + void markLMSAvailability(final String externalQuizId, final boolean available, final String updateId); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java index 06e74fb9..adaf45e2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java @@ -9,6 +9,8 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao; import java.util.Arrays; +import java.util.List; +import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; @@ -17,6 +19,7 @@ import org.springframework.util.MultiValueMap; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; +import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; @@ -58,7 +61,7 @@ public class FilterMap extends POSTMapper { } public Integer getActiveAsInt() { - return getBooleanAsInteger(UserInfo.FILTER_ATTR_ACTIVE); + return getBooleanAsInteger(Entity.FILTER_ATTR_ACTIVE); } public Long getInstitutionId() { @@ -106,7 +109,7 @@ public class FilterMap extends POSTMapper { } public DateTime getExamFromTime() { - return Utils.toDateTime(getString(QuizData.FILTER_ATTR_START_TIME)); + return Utils.toDateTime(getString(Domain.EXAM.ATTR_QUIZ_START_TIME)); } public DateTime getSEBClientConfigFromTime() { @@ -154,7 +157,7 @@ public class FilterMap extends POSTMapper { } public String getConfigAttributeType() { - return getSQLWildcard(ConfigurationAttribute.FILTER_ATTR_TYPE); + return getString(ConfigurationAttribute.FILTER_ATTR_TYPE); } public Long getConfigValueConfigId() { @@ -205,6 +208,15 @@ public class FilterMap extends POSTMapper { return Utils.toSQLWildcard(this.params.getFirst(name)); } + public List getClientConnectionTokenList() { + final String tokenList = getString(ClientConnection.FILTER_ATTR_TOKEN_LIST); + if (StringUtils.isBlank(tokenList)) { + return null; + } + + return Utils.asImmutableList(StringUtils.split(tokenList, Constants.LIST_SEPARATOR)); + } + public Long getClientConnectionExamId() { return getLong(ClientConnection.FILTER_ATTR_EXAM_ID); } @@ -333,4 +345,11 @@ public class FilterMap extends POSTMapper { } } + public boolean containsAny(final Set extFilter) { + return extFilter.stream() + .filter(this.params::containsKey) + .findFirst() + .isPresent(); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/NoResourceFoundException.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/NoResourceFoundException.java new file mode 100644 index 00000000..07ed3de3 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/NoResourceFoundException.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.dao; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +import ch.ethz.seb.sebserver.gbl.api.EntityType; + +@ResponseStatus(HttpStatus.NOT_FOUND) +public class NoResourceFoundException extends RuntimeException { + + private static final long serialVersionUID = 4347712679241097195L; + public final EntityType entityType; + + public NoResourceFoundException(final EntityType entityType, final String message) { + super("Resource " + entityType + " not found: " + message); + this.entityType = entityType; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/SEBClientConfigDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/SEBClientConfigDAO.java index 0d803429..3717f378 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/SEBClientConfigDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/SEBClientConfigDAO.java @@ -8,17 +8,10 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao; -import java.util.Collection; -import java.util.Set; - -import org.springframework.cache.annotation.CacheEvict; - import ch.ethz.seb.sebserver.gbl.client.ClientCredentials; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO; -import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ClientConfigService; /** Concrete EntityDAO interface of SEBClientConfig entities */ public interface SEBClientConfigDAO extends @@ -54,10 +47,4 @@ public interface SEBClientConfigDAO extends * @return encrypted configuration password */ Result getConfigPasswordCipherByClientName(String clientName); - @Override - @CacheEvict( - cacheNames = ClientConfigService.EXAM_CLIENT_DETAILS_CACHE, - allEntries = true) - Result> delete(Set all); - } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java index 34ece538..4e2f0c99 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java @@ -78,6 +78,12 @@ public interface UserActivityLogDAO extends * @return Result of the Entity or referring to an Error if happened */ Result logModify(E entity); + /** Create a user activity log entry for the current user of activity type FINISHED + * + * @param entity the Entity + * @return Result of the Entity or referring to an Error if happened */ + Result logFinished(E entity); + /** Create a user activity log entry for the current user of activity type DELETE * * @param entity the Entity diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/AdditionalAttributesDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/AdditionalAttributesDAOImpl.java index c963e9a2..d3643909 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/AdditionalAttributesDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/AdditionalAttributesDAOImpl.java @@ -21,6 +21,7 @@ import org.springframework.transaction.annotation.Transactional; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.AdditionalAttributeRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.AdditionalAttributeRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord; @@ -124,7 +125,7 @@ public class AdditionalAttributesDAOImpl implements AdditionalAttributesDAO { type.name(), entityId, name, - value); + Utils.truncateText(value, 4000)); this.additionalAttributeRecordMapper .updateByPrimaryKeySelective(rec); @@ -136,7 +137,7 @@ public class AdditionalAttributesDAOImpl implements AdditionalAttributesDAO { type.name(), entityId, name, - value); + Utils.truncateText(value, 4000)); this.additionalAttributeRecordMapper .insert(rec); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/BatchActionDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/BatchActionDAOImpl.java index 6956dad4..b3c23cec 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/BatchActionDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/BatchActionDAOImpl.java @@ -16,6 +16,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -28,16 +29,23 @@ import org.springframework.transaction.annotation.Transactional; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.model.BatchAction; import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityDependency; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.BatchActionRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.BatchActionRecordMapper; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.BatchActionRecord; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.BatchActionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DAOLoggingSupport; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; @@ -49,10 +57,20 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler; @WebServiceProfile public class BatchActionDAOImpl implements BatchActionDAO { - private final BatchActionRecordMapper batchActionRecordMapper; + private static final long ABANDONED_BATCH_TIME = Constants.MINUTE_IN_MILLIS * 10; + + private final BatchActionRecordMapper batchActionRecordMapper; + private final AdditionalAttributesDAO additionalAttributesDAO; + private final JSONMapper jsonMapper; + + public BatchActionDAOImpl( + final BatchActionRecordMapper batchActionRecordMapper, + final AdditionalAttributesDAO additionalAttributesDAO, + final JSONMapper jsonMapper) { - public BatchActionDAOImpl(final BatchActionRecordMapper batchActionRecordMapper) { this.batchActionRecordMapper = batchActionRecordMapper; + this.additionalAttributesDAO = additionalAttributesDAO; + this.jsonMapper = jsonMapper; } @Override @@ -65,22 +83,18 @@ public class BatchActionDAOImpl implements BatchActionDAO { public Result getAndReserveNext(final String processId) { return Result.tryCatch(() -> { - final Long oldTherhold = Utils.getMillisecondsNow() - Constants.HOUR_IN_MILLIS; - final List next = this.batchActionRecordMapper.selectByExample() + final Long oldThreshold = Utils.getMillisecondsNow() - ABANDONED_BATCH_TIME; + final BatchActionRecord nextRec = this.batchActionRecordMapper.selectByExample() .where(BatchActionRecordDynamicSqlSupport.lastUpdate, isNull()) - .and(BatchActionRecordDynamicSqlSupport.processorId, isNull()) - .or(BatchActionRecordDynamicSqlSupport.lastUpdate, isLessThan(oldTherhold)) + .or(BatchActionRecordDynamicSqlSupport.processorId, isNotLike("%" + BatchAction.FINISHED_FLAG)) .build() - .execute(); - - if (next == null || next.isEmpty()) { - - throw new ResourceNotFoundException( - EntityType.BATCH_ACTION, - processId); - } - - final BatchActionRecord nextRec = next.get(0); + .execute() + .stream() + .filter(rec -> rec.getLastUpdate() == null || rec.getLastUpdate() < oldThreshold) + .findFirst() + .orElseThrow(() -> new ResourceNotFoundException( + EntityType.BATCH_ACTION, + processId)); final BatchActionRecord newRecord = new BatchActionRecord( nextRec.getId(), @@ -88,6 +102,8 @@ public class BatchActionDAOImpl implements BatchActionDAO { null, null, null, + null, + null, Utils.getMillisecondsNow(), processId); @@ -98,42 +114,6 @@ public class BatchActionDAOImpl implements BatchActionDAO { .onError(TransactionHandler::rollback); } - @Override - @Transactional - public Result updateProgress( - final Long actionId, - final String processId, - final Collection modelIds) { - - return Result.tryCatch(() -> { - - final BatchActionRecord rec = this.batchActionRecordMapper.selectByPrimaryKey(actionId); - - if (!processId.equals(rec.getProcessorId())) { - throw new RuntimeException("Batch action processor id mismatch: " + processId + " " + rec); - } - - final Set ids = new HashSet<>(Arrays.asList(StringUtils.split( - rec.getSuccessful(), - Constants.LIST_SEPARATOR))); - ids.addAll(modelIds); - - final BatchActionRecord newRecord = new BatchActionRecord( - actionId, - null, - null, - null, - StringUtils.join(ids, Constants.LIST_SEPARATOR), - Utils.getMillisecondsNow(), - processId); - - this.batchActionRecordMapper.updateByPrimaryKeySelective(newRecord); - return this.batchActionRecordMapper.selectByPrimaryKey(actionId); - }) - .flatMap(this::toDomainModel) - .onError(TransactionHandler::rollback); - } - @Override @Transactional public void setSuccessfull(final Long actionId, final String processId, final String modelId) { @@ -145,18 +125,54 @@ public class BatchActionDAOImpl implements BatchActionDAO { throw new RuntimeException("Batch action processor id mismatch: " + processId + " " + rec); } + String successful = rec.getSuccessful(); + if (StringUtils.isBlank(successful)) { + successful = modelId; + } else { + final Set ids = new HashSet<>(Arrays.asList(StringUtils.split( + successful, + Constants.LIST_SEPARATOR))); + ids.add(modelId); + successful = StringUtils.join(ids, Constants.LIST_SEPARATOR); + } + final BatchActionRecord newRecord = new BatchActionRecord( actionId, null, null, null, - rec.getSuccessful() + Constants.LIST_SEPARATOR + modelId, + null, + null, + successful, Utils.getMillisecondsNow(), processId); this.batchActionRecordMapper.updateByPrimaryKeySelective(newRecord); } catch (final Exception e) { - log.error("Failed to mark entity sucessfuly processed: modelId: {}, processId"); + log.error("Failed to mark entity successfully processed: modelId: {}, processId", modelId, e); + } + } + + @Override + @Transactional + public void setFailure(final Long actionId, final String processId, final String modelId, final Exception error) { + try { + String apiMessage = null; + if (error instanceof APIMessageException) { + apiMessage = this.jsonMapper.writeValueAsString(((APIMessageException) error).getMainMessage()); + } else { + apiMessage = this.jsonMapper.writeValueAsString(APIMessage.ErrorMessage.UNEXPECTED.of(error)); + } + + this.additionalAttributesDAO + .saveAdditionalAttribute(EntityType.BATCH_ACTION, actionId, modelId, apiMessage) + .onError(err -> log.error("Failed to store batch action failure: actionId: {}, modelId: {}", + actionId, + modelId, + err)); + + } catch (final Exception e) { + log.error("Unexpected error while trying to persist batch action error: ", e); } } @@ -177,13 +193,22 @@ public class BatchActionDAOImpl implements BatchActionDAO { rec.getSourceIds(), Constants.LIST_SEPARATOR))); - final Set success = new HashSet<>(Arrays.asList(StringUtils.split( - rec.getSourceIds(), - Constants.LIST_SEPARATOR))); + // get all succeeded + final String successful = rec.getSuccessful(); + final Set success = StringUtils.isNotBlank(successful) + ? new HashSet<>(Arrays.asList(StringUtils.split( + successful, + Constants.LIST_SEPARATOR))) + : Collections.emptySet(); - if (ids.size() != success.size()) { + // get all failed + final Collection failed = this.additionalAttributesDAO + .getAdditionalAttributes(EntityType.BATCH_ACTION, actionId) + .getOrThrow(); + + if (ids.size() != success.size() + failed.size()) { throw new IllegalStateException( - "Processing ids mismatch source: " + ids + " success: " + success); + "Processing ids mismatch source: " + ids + " success: " + success + " failed: " + failed); } } @@ -193,8 +218,10 @@ public class BatchActionDAOImpl implements BatchActionDAO { null, null, null, + null, + null, Utils.getMillisecondsNow(), - processId + FLAG_FINISHED); + processId + BatchAction.FINISHED_FLAG); this.batchActionRecordMapper.updateByPrimaryKeySelective(newRecord); return this.batchActionRecordMapper.selectByPrimaryKey(actionId); @@ -207,7 +234,7 @@ public class BatchActionDAOImpl implements BatchActionDAO { @Transactional(readOnly = true) public Result byPK(final Long id) { return recordById(id) - .flatMap(this::toDomainModel); + .flatMap(this::toDomainModelWithFailures); } @Override @@ -261,7 +288,9 @@ public class BatchActionDAOImpl implements BatchActionDAO { final BatchActionRecord newRecord = new BatchActionRecord( null, data.institutionId, + data.ownerId, data.actionType.toString(), + data.attributes != null ? this.jsonMapper.writeValueAsString(data.attributes) : null, StringUtils.join(data.sourceIds, Constants.LIST_SEPARATOR), null, null, null); @@ -282,6 +311,8 @@ public class BatchActionDAOImpl implements BatchActionDAO { null, null, null, + null, + null, StringUtils.join(data.successful, Constants.LIST_SEPARATOR), data.getLastUpdate(), data.processorId); @@ -293,6 +324,17 @@ public class BatchActionDAOImpl implements BatchActionDAO { .onError(TransactionHandler::rollback); } + @Override + @Transactional(readOnly = true) + public Set getDependencies(final BulkAction bulkAction) { + // all of institution + if (bulkAction.sourceType == EntityType.INSTITUTION) { + return getDependencies(bulkAction, this::allIdsOfInstitution); + } + + return Collections.emptySet(); + } + @Override @Transactional public Result> delete(final Set all) { @@ -304,6 +346,15 @@ public class BatchActionDAOImpl implements BatchActionDAO { return Collections.emptyList(); } + // try delete all additional attributes first + ids.stream().forEach(id -> { + try { + this.additionalAttributesDAO.deleteAll(EntityType.BATCH_ACTION, id); + } catch (final Exception e) { + log.error("Failed to delete additional attributes for batch action: {}", id, e); + } + }); + this.batchActionRecordMapper.deleteByExample() .where(BatchActionRecordDynamicSqlSupport.id, isIn(ids)) .build() @@ -327,15 +378,73 @@ public class BatchActionDAOImpl implements BatchActionDAO { }); } + private Result toDomainModelWithFailures(final BatchActionRecord record) { + return toDomainModel(record, true); + } + private Result toDomainModel(final BatchActionRecord record) { + return toDomainModel(record, false); + } + + private Result toDomainModel(final BatchActionRecord record, final boolean withFailures) { + final String successful = record.getSuccessful(); + + Map failures = Collections.emptyMap(); + try { + failures = this.additionalAttributesDAO + .getAdditionalAttributes(EntityType.BATCH_ACTION, record.getId()) + .getOrThrow() + .stream() + .collect(Collectors.toMap(this::toEntityKey, this::toFailureMessage)); + } catch (final Exception e) { + log.error("Failed to get batch action failure messages", e); + } + + final Map failuresMap = failures; return Result.tryCatch(() -> new BatchAction( record.getId(), record.getInstitutionId(), + record.getOwner(), BatchActionType.valueOf(record.getActionType()), + Utils.jsonToMap(record.getAttributes(), this.jsonMapper), Arrays.asList(record.getSourceIds().split(Constants.LIST_SEPARATOR)), - Arrays.asList(record.getSuccessful().split(Constants.LIST_SEPARATOR)), + StringUtils.isNoneBlank(successful) ? Arrays.asList(successful.split(Constants.LIST_SEPARATOR)) : null, record.getLastUpdate(), - record.getProcessorId())); + record.getProcessorId(), + failuresMap)); + } + + private String toEntityKey(final AdditionalAttributeRecord rec) { + try { + return rec.getName(); + } catch (final Exception e) { + log.error("Failed to parse entity key for batch action failure: {}", e.getMessage()); + return "-1"; + } + } + + private APIMessage toFailureMessage(final AdditionalAttributeRecord rec) { + try { + return this.jsonMapper.readValue(rec.getValue(), APIMessage.class); + } catch (final Exception e) { + log.error("Failed to parse APIMessage for batch action failure: {}", e.getMessage()); + return APIMessage.ErrorMessage.UNEXPECTED.of(e); + } + } + + private Result> allIdsOfInstitution(final EntityKey institutionKey) { + return Result.tryCatch(() -> this.batchActionRecordMapper.selectByExample() + .where(BatchActionRecordDynamicSqlSupport.institutionId, + isEqualTo(Long.valueOf(institutionKey.modelId))) + .build() + .execute() + .stream() + .map(rec -> new EntityDependency( + institutionKey, + new EntityKey(rec.getId(), EntityType.BATCH_ACTION), + rec.getActionType(), + rec.getOwner())) + .collect(Collectors.toList())); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/CertificateDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/CertificateDAOImpl.java index 1b80ef9e..88455581 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/CertificateDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/CertificateDAOImpl.java @@ -8,6 +8,9 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl; +import static org.mybatis.dynamic.sql.SqlBuilder.isEqualTo; +import static org.mybatis.dynamic.sql.SqlBuilder.isIn; + import java.io.ByteArrayInputStream; import java.security.KeyStoreException; import java.security.PrivateKey; @@ -21,6 +24,7 @@ import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.NoSuchElementException; +import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.io.IOUtils; @@ -43,6 +47,7 @@ import org.springframework.transaction.annotation.Transactional; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.EntityDependency; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.sebconfig.CertificateInfo; import ch.ethz.seb.sebserver.gbl.model.sebconfig.CertificateInfo.CertificateType; @@ -54,7 +59,9 @@ import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.CertificateRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.CertificateRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.CertificateRecord; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.CertificateDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler; @@ -76,6 +83,11 @@ public class CertificateDAOImpl implements CertificateDAO { this.cryptor = cryptor; } + @Override + public EntityType entityType() { + return EntityType.CERTIFICATE; + } + @Override @Transactional(readOnly = true) public Result getCertificate(final Long institutionId, final String alias) { @@ -135,6 +147,17 @@ public class CertificateDAOImpl implements CertificateDAO { .onError(TransactionHandler::rollback); } + @Override + @Transactional(readOnly = true) + public Set getDependencies(final BulkAction bulkAction) { + // all of institution + if (bulkAction.sourceType == EntityType.INSTITUTION) { + return getDependencies(bulkAction, this::allIdsOfInstitution); + } + + return Collections.emptySet(); + } + @Override @Transactional(readOnly = true) public Result> getAllIdentityAlias(final Long institutionId) { @@ -177,6 +200,28 @@ public class CertificateDAOImpl implements CertificateDAO { .collect(Collectors.toList())); } + @Override + @Transactional + public Result> delete(final Set all) { + return Result.tryCatch(() -> { + + final List ids = new ArrayList<>(EntityDAO.extractPKsFromKeys(all, EntityType.CERTIFICATE)); + + if (ids.isEmpty()) { + return Collections.emptyList(); + } + + this.certificateRecordMapper.deleteByExample() + .where(CertificateRecordDynamicSqlSupport.id, isIn(ids)) + .build() + .execute(); + + return ids.stream() + .map(id -> new EntityKey(id, EntityType.CERTIFICATE)) + .collect(Collectors.toList()); + }); + } + @Override public String extractAlias(final X509Certificate certificate, final String alias) { if (StringUtils.isNotBlank(alias)) { @@ -194,7 +239,7 @@ public class CertificateDAOImpl implements CertificateDAO { return dn.replace(" ", "_").toLowerCase(); } } catch (final CertificateEncodingException e) { - log.warn("Error while trying to get alias form certificate subject name. Use serial number as alias"); + log.warn("Error while trying to get alias from certificate subject name. Use serial number as alias"); return String.valueOf(certificate.getSerialNumber()); } } @@ -415,4 +460,19 @@ public class CertificateDAOImpl implements CertificateDAO { .flatMap(input -> this.cryptor.loadKeyStore(input)); } + private Result> allIdsOfInstitution(final EntityKey institutionKey) { + return Result.tryCatch(() -> this.certificateRecordMapper.selectByExample() + .where(CertificateRecordDynamicSqlSupport.institutionId, + isEqualTo(Long.valueOf(institutionKey.modelId))) + .build() + .execute() + .stream() + .map(rec -> new EntityDependency( + institutionKey, + new EntityKey(rec.getId(), EntityType.CERTIFICATE), + rec.getAliases(), + rec.getAliases())) + .collect(Collectors.toList())); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java index 009fae8a..fb3732d8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java @@ -24,6 +24,7 @@ import org.apache.commons.lang3.BooleanUtils; import org.mybatis.dynamic.sql.SqlBuilder; import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter; import org.mybatis.dynamic.sql.select.QueryExpressionDSL; +import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -38,6 +39,7 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.ClientConnectionTokenMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport; @@ -70,19 +72,25 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { private final ClientInstructionRecordMapper clientInstructionRecordMapper; private final ClientIndicatorRecordMapper clientIndicatorRecordMapper; private final ClientNotificationRecordMapper clientNotificationRecordMapper; + private final ClientConnectionTokenMapper clientConnectionMinMapper; + private final CacheManager cacheManager; protected ClientConnectionDAOImpl( final ClientConnectionRecordMapper clientConnectionRecordMapper, final ClientEventRecordMapper clientEventRecordMapper, final ClientInstructionRecordMapper clientInstructionRecordMapper, final ClientIndicatorRecordMapper clientIndicatorRecordMapper, - final ClientNotificationRecordMapper clientNotificationRecordMapper) { + final ClientNotificationRecordMapper clientNotificationRecordMapper, + final ClientConnectionTokenMapper clientConnectionMinMapper, + final CacheManager cacheManager) { this.clientConnectionRecordMapper = clientConnectionRecordMapper; this.clientEventRecordMapper = clientEventRecordMapper; this.clientInstructionRecordMapper = clientInstructionRecordMapper; this.clientIndicatorRecordMapper = clientIndicatorRecordMapper; this.clientNotificationRecordMapper = clientNotificationRecordMapper; + this.clientConnectionMinMapper = clientConnectionMinMapper; + this.cacheManager = cacheManager; } @Override @@ -121,6 +129,9 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { ClientConnectionRecordDynamicSqlSupport.institutionId, isEqualToWhenPresent(filterMap.getInstitutionId())); return whereClause + .and( + ClientConnectionRecordDynamicSqlSupport.connectionToken, + isInWhenPresent(filterMap.getClientConnectionTokenList())) .and( ClientConnectionRecordDynamicSqlSupport.examId, isEqualToWhenPresent(filterMap.getClientConnectionExamId())) @@ -162,7 +173,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { @Override @Transactional(readOnly = true) public Result> getConnectionTokens(final Long examId) { - return Result.tryCatch(() -> this.clientConnectionRecordMapper + return Result.tryCatch(() -> this.clientConnectionMinMapper .selectByExample() .where( ClientConnectionRecordDynamicSqlSupport.examId, @@ -170,15 +181,15 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { .build() .execute() .stream() - .map(ClientConnectionRecord::getConnectionToken) + .map(rec -> rec.connection_token) .filter(StringUtils::isNotBlank) .collect(Collectors.toList())); } @Override @Transactional(readOnly = true) - public Result> getActiveConnctionTokens(final Long examId) { - return Result.tryCatch(() -> this.clientConnectionRecordMapper + public Result> getActiveConnectionTokens(final Long examId) { + return Result.tryCatch(() -> this.clientConnectionMinMapper .selectByExample() .where( ClientConnectionRecordDynamicSqlSupport.examId, @@ -189,7 +200,26 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { .build() .execute() .stream() - .map(ClientConnectionRecord::getConnectionToken) + .map(rec -> rec.connection_token) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toList())); + } + + @Override + @Transactional(readOnly = true) + public Result> getAllActiveConnectionTokens(final Long examId) { + return Result.tryCatch(() -> this.clientConnectionMinMapper + .selectByExample() + .where( + ClientConnectionRecordDynamicSqlSupport.examId, + SqlBuilder.isEqualTo(examId)) + .and( + ClientConnectionRecordDynamicSqlSupport.status, + SqlBuilder.isIn(ClientConnection.ACTIVE_STATES)) + .build() + .execute() + .stream() + .map(rec -> rec.connection_token) .filter(StringUtils::isNotBlank) .collect(Collectors.toList())); } @@ -328,6 +358,11 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { return Result.tryCatch(() -> { final long millisecondsNow = Utils.getMillisecondsNow(); + // NOTE: we use nanoseconds here to get a better precision to better avoid + // same value of real concurrent calls on distributed systems + // TODO: Better solution for the future would be to count this value and + // isolation is done via DB transaction + final long nanosecondsNow = System.nanoTime(); final ClientConnectionRecord newRecord = new ClientConnectionRecord( null, data.institutionId, @@ -340,7 +375,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { BooleanUtils.toInteger(data.vdi, 1, 0, 0), data.vdiPairToken, millisecondsNow, - millisecondsNow, + nanosecondsNow, data.remoteProctoringRoomId, null, Utils.truncateText(data.sebMachineName, 255), @@ -359,7 +394,11 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { public Result save(final ClientConnection data) { return Result.tryCatch(() -> { - final long millisecondsNow = Utils.getMillisecondsNow(); + // NOTE: we use nanoseconds here to get a better precision to better avoid + // same value of real concurrent calls on distributed systems + // TODO: Better solution for the future would be to count this value and + // isolation is done via DB transaction + final long nanosecondsNow = System.nanoTime(); final ClientConnectionRecord updateRecord = new ClientConnectionRecord( data.id, null, @@ -372,7 +411,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { BooleanUtils.toInteger(data.vdi, 1, 0, 0), data.vdiPairToken, null, - millisecondsNow, + nanosecondsNow, null, null, Utils.truncateText(data.sebMachineName, 255), @@ -494,6 +533,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { return Collections.emptyList(); } + ids.stream().forEach(this::clearConnecionTokenCache); + // delete all related client indicators this.clientIndicatorRecordMapper.deleteByExample() .where( @@ -844,4 +885,22 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { return record.getConnectionToken(); } + private Long clearConnecionTokenCache(final Long id) { + + try { + + final ClientConnectionRecord rec = recordById(id) + .getOrThrow(); + + this.cacheManager + .getCache(CONNECTION_TOKENS_CACHE) + .evictIfPresent(rec.getExamId()); + + } catch (final Exception e) { + log.error("Failed to clear connection token cache: ", e); + } + + return id; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationAttributeDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationAttributeDAOImpl.java index cc3b6697..a7a6964e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationAttributeDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationAttributeDAOImpl.java @@ -177,7 +177,7 @@ public class ConfigurationAttributeDAOImpl implements ConfigurationAttributeDAO final ConfigurationAttributeRecord newRecord = new ConfigurationAttributeRecord( data.id, data.name, - data.type.name(), + data.type != null ? data.type.name() : null, data.parentId, data.resources, data.validator, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOBatchService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOBatchService.java index a7790ca7..d949e137 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOBatchService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOBatchService.java @@ -57,6 +57,7 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ConfigurationAttri import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ConfigurationNodeRecord; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ConfigurationRecord; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ConfigurationValueRecord; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DAOUserServcie; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ExamConfigInitService; @@ -78,10 +79,11 @@ class ConfigurationDAOBatchService { private final ConfigurationAttributeRecordMapper batchConfigurationAttributeRecordMapper; private final ConfigurationRecordMapper batchConfigurationRecordMapper; private final ExamConfigInitService examConfigInitService; - private final SqlSessionTemplate batchSqlSessionTemplate; + private final DAOUserServcie daoUserServcie; protected ConfigurationDAOBatchService( + final DAOUserServcie daoUserServcie, @Qualifier(BatisConfig.SQL_BATCH_SESSION_TEMPLATE) final SqlSessionTemplate batchSqlSessionTemplate, final ExamConfigInitService examConfigInitService) { @@ -117,7 +119,7 @@ class ConfigurationDAOBatchService { this.batchConfigurationRecordMapper = batchSqlSessionTemplate.getMapper(ConfigurationRecordMapper.class); this.batchSqlSessionTemplate = batchSqlSessionTemplate; - + this.daoUserServcie = daoUserServcie; } Result createNewConfiguration(final ConfigurationNode data) { @@ -148,7 +150,9 @@ class ConfigurationDAOBatchService { data.name, data.description, data.type.name(), - (data.status != null) ? data.status.name() : ConfigurationStatus.CONSTRUCTION.name()); + (data.status != null) ? data.status.name() : ConfigurationStatus.CONSTRUCTION.name(), + Utils.getMillisecondsNow(), + this.daoUserServcie.getCurrentUserUUID()); this.batchConfigurationNodeRecordMapper.insert(newRecord); this.batchSqlSessionTemplate.flushStatements(); @@ -397,7 +401,9 @@ class ConfigurationDAOBatchService { copyInfo.getName(), copyInfo.getDescription(), copyInfo.configurationType.name(), - ConfigurationStatus.CONSTRUCTION.name()); + ConfigurationStatus.CONSTRUCTION.name(), + Utils.getMillisecondsNow(), + this.daoUserServcie.getCurrentUserUUID()); this.batchConfigurationNodeRecordMapper.insert(newNodeRec); this.batchSqlSessionTemplate.flushStatements(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java index 36dfed99..6e3e6409 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java @@ -8,6 +8,8 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl; +import static ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordDynamicSqlSupport.configurationTemplateId; +import static ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordDynamicSqlSupport.examTemplateRecord; import static org.mybatis.dynamic.sql.SqlBuilder.*; import java.util.ArrayList; @@ -23,6 +25,7 @@ import org.apache.commons.lang3.StringUtils; import org.mybatis.dynamic.sql.SqlBuilder; import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter; import org.mybatis.dynamic.sql.select.QueryExpressionDSL; +import org.mybatis.dynamic.sql.update.UpdateDSL; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -38,6 +41,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.Configuration import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationAttributeRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationNodeRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationNodeRecordMapper; @@ -45,11 +49,18 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationReco import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationValueRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationValueRecordMapper; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordDynamicSqlSupport; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.InstitutionRecordDynamicSqlSupport; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.OrientationRecordDynamicSqlSupport; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.OrientationRecordMapper; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ViewRecordDynamicSqlSupport; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ViewRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ConfigurationNodeRecord; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DAOLoggingSupport; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DAOUserServcie; 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.TransactionHandler; @@ -63,18 +74,30 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO { private final ConfigurationNodeRecordMapper configurationNodeRecordMapper; private final ConfigurationValueRecordMapper configurationValueRecordMapper; private final ConfigurationDAOBatchService configurationDAOBatchService; + private final ExamTemplateRecordMapper examTemplateRecordMapper; + private final ViewRecordMapper viewRecordMapper; + private final OrientationRecordMapper orientationRecordMapper; + private final DAOUserServcie daoUserServcie; protected ConfigurationNodeDAOImpl( final ConfigurationRecordMapper configurationRecordMapper, final ConfigurationNodeRecordMapper configurationNodeRecordMapper, final ConfigurationValueRecordMapper configurationValueRecordMapper, final ConfigurationAttributeRecordMapper configurationAttributeRecordMapper, - final ConfigurationDAOBatchService ConfigurationDAOBatchService) { + final ConfigurationDAOBatchService ConfigurationDAOBatchService, + final ExamTemplateRecordMapper examTemplateRecordMapper, + final ViewRecordMapper viewRecordMapper, + final OrientationRecordMapper orientationRecordMapper, + final DAOUserServcie daoUserServcie) { this.configurationRecordMapper = configurationRecordMapper; this.configurationNodeRecordMapper = configurationNodeRecordMapper; this.configurationValueRecordMapper = configurationValueRecordMapper; this.configurationDAOBatchService = ConfigurationDAOBatchService; + this.examTemplateRecordMapper = examTemplateRecordMapper; + this.viewRecordMapper = viewRecordMapper; + this.orientationRecordMapper = orientationRecordMapper; + this.daoUserServcie = daoUserServcie; } @Override @@ -215,7 +238,9 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO { data.name, data.description, null, - (data.status != null) ? data.status.name() : ConfigurationStatus.CONSTRUCTION.name()); + (data.status != null) ? data.status.name() : ConfigurationStatus.CONSTRUCTION.name(), + Utils.getMillisecondsNow(), + this.daoUserServcie.getCurrentUserUUID()); this.configurationNodeRecordMapper.updateByPrimaryKeySelective(newRecord); return this.configurationNodeRecordMapper.selectByPrimaryKey(data.id); @@ -249,22 +274,28 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO { } // find all configurations for this configuration node - final List configurationIds = this.configurationRecordMapper.selectIdsByExample() + final List configurationIds = this.configurationRecordMapper + .selectIdsByExample() .where(ConfigurationRecordDynamicSqlSupport.configurationNodeId, isIn(ids)) .build() .execute(); - // delete all ConfigurationValue's that belongs to the Configuration's to delete - this.configurationValueRecordMapper.deleteByExample() - .where(ConfigurationValueRecordDynamicSqlSupport.configurationId, isIn(configurationIds)) - .build() - .execute(); + if (!configurationIds.isEmpty()) { - // delete all Configuration's - this.configurationRecordMapper.deleteByExample() - .where(ConfigurationRecordDynamicSqlSupport.id, isIn(configurationIds)) - .build() - .execute(); + // delete all ConfigurationValue's that belongs to the Configuration's to delete + this.configurationValueRecordMapper.deleteByExample() + .where(ConfigurationValueRecordDynamicSqlSupport.configurationId, isIn(configurationIds)) + .build() + .execute(); + + // delete all Configuration's + this.configurationRecordMapper.deleteByExample() + .where(ConfigurationRecordDynamicSqlSupport.id, isIn(configurationIds)) + .build() + .execute(); + } + + handleConfigTemplateDeletion(ids); // and finally delete the requested ConfigurationNode's this.configurationNodeRecordMapper.deleteByExample() @@ -278,6 +309,46 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO { }); } + private void handleConfigTemplateDeletion(final List configurationIds) { + // get all config template node ids + final List templatesIds = this.configurationNodeRecordMapper + .selectIdsByExample() + .where(ConfigurationNodeRecordDynamicSqlSupport.id, isIn(configurationIds)) + .and(ConfigurationNodeRecordDynamicSqlSupport.type, isEqualTo(ConfigurationType.TEMPLATE.name())) + .build() + .execute(); + + if (templatesIds == null || templatesIds.isEmpty()) { + return; + } + + // delete all related views and orientations + this.orientationRecordMapper.deleteByExample() + .where(OrientationRecordDynamicSqlSupport.templateId, isIn(templatesIds)) + .build() + .execute(); + this.viewRecordMapper.deleteByExample() + .where(ViewRecordDynamicSqlSupport.templateId, isIn(templatesIds)) + .build() + .execute(); + + // update all config nodes that uses one of the templates + this.configurationNodeRecordMapper.updateByExampleSelective( + new ConfigurationNodeRecord(null, null, 0L, null, null, null, null, null, + Utils.getMillisecondsNow(), + this.daoUserServcie.getCurrentUserUUID())) + .where(ConfigurationNodeRecordDynamicSqlSupport.templateId, isIn(configurationIds)) + .build() + .execute(); + + // update all examTemplates that uses one of the templates + UpdateDSL.updateWithMapper(this.examTemplateRecordMapper::update, examTemplateRecord) + .set(configurationTemplateId).equalToNull() + .where(ExamTemplateRecordDynamicSqlSupport.configurationTemplateId, isIn(configurationIds)) + .build() + .execute(); + } + private Result> allIdsOfInstitution(final EntityKey institutionKey) { return Result.tryCatch(() -> this.configurationNodeRecordMapper.selectByExample() .where( @@ -358,7 +429,9 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO { record.getDescription(), ConfigurationType.valueOf(record.getType()), record.getOwner(), - ConfigurationStatus.valueOf(record.getStatus()))); + ConfigurationStatus.valueOf(record.getStatus()), + Utils.toDateTimeUTC(record.getLastUpdateTime()), + record.getLastUpdateUser())); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/DAOUserServcieImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/DAOUserServcieImpl.java new file mode 100644 index 00000000..162b31c8 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/DAOUserServcieImpl.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DAOUserServcie; + +@Lazy +@Service +@WebServiceProfile +public class DAOUserServcieImpl implements DAOUserServcie { + + private static final Logger log = LoggerFactory.getLogger(DAOUserServcieImpl.class); + + private final AuthorizationService authorizationService; + + public DAOUserServcieImpl(final AuthorizationService authorizationService) { + this.authorizationService = authorizationService; + } + + @Override + public String getCurrentUserUUID() { + try { + return this.authorizationService.getUserService().getCurrentUser().uuid(); + } catch (final Exception e) { + log.error("Failed to get current user: ", e); + return null; + } + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java index ff364d13..5d4c30ad 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java @@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl; import static org.mybatis.dynamic.sql.SqlBuilder.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -64,6 +65,10 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler; @WebServiceProfile public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { + private static final List ACTIVE_EXAM_STATE_NAMES = Arrays.asList( + ExamStatus.FINISHED.name(), + ExamStatus.ARCHIVED.name()); + private final ExamRecordMapper examRecordMapper; private final ExamConfigurationMapRecordMapper examConfigurationMapRecordMapper; private final ConfigurationNodeRecordMapper configurationNodeRecordMapper; @@ -344,7 +349,8 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { @Override @Transactional(readOnly = true) public Result> getExamIdsForConfigNodeId(final Long configurationNodeId) { - return Result.tryCatch(() -> this.examConfigurationMapRecordMapper.selectByExample() + return Result.tryCatch(() -> this.examConfigurationMapRecordMapper + .selectByExample() .where( ExamConfigurationMapRecordDynamicSqlSupport.configurationNodeId, isEqualTo(configurationNodeId)) @@ -383,16 +389,16 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { .build() .execute() .stream() - .filter(rec -> !isExamFinished(rec.getExamId())) + .filter(rec -> !isExamActive(rec.getExamId())) .findFirst() .isPresent()); } - private boolean isExamFinished(final Long examId) { + private boolean isExamActive(final Long examId) { try { return this.examRecordMapper.countByExample() .where(ExamRecordDynamicSqlSupport.id, isEqualTo(examId)) - .and(ExamRecordDynamicSqlSupport.status, isEqualTo(ExamStatus.FINISHED.name())) + .and(ExamRecordDynamicSqlSupport.status, isIn(ACTIVE_EXAM_STATE_NAMES)) .build() .execute() >= 1; } catch (final Exception e) { @@ -439,9 +445,10 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { record.getInstitutionId(), record.getExamId(), (exam != null) ? exam.name : null, - (exam != null) ? exam.description : null, + (exam != null) ? exam.getDescription() : null, (exam != null) ? exam.startTime : null, (exam != null) ? exam.type : ExamType.UNDEFINED, + (exam != null) ? exam.status : null, record.getConfigurationNodeId(), record.getUserNames(), record.getEncryptSecret(), @@ -610,7 +617,9 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { null, null, null, - ConfigurationStatus.READY_TO_USE.name()); + ConfigurationStatus.READY_TO_USE.name(), + null, + null); this.configurationNodeRecordMapper.updateByPrimaryKeySelective(newRecord); return id; } else { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java index f9f1d6bb..dd17c5ff 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java @@ -16,10 +16,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; @@ -27,16 +25,17 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; -import org.joda.time.DateTime; -import org.mybatis.dynamic.sql.SqlBuilder; import org.mybatis.dynamic.sql.update.UpdateDSL; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityDependency; import ch.ethz.seb.sebserver.gbl.model.EntityKey; @@ -45,50 +44,38 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; 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.LmsSetup.LmsType; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Utils; -import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.AdditionalAttributeRecordDynamicSqlSupport; -import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.AdditionalAttributeRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordMapper; -import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ExamRecord; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleCourseAccess; @Lazy @Component @WebServiceProfile public class ExamDAOImpl implements ExamDAO { - public static final String FAILED_TO_LOAD_QUIZ_DATA_MARK = "[FAILED TO LOAD DATA FROM LMS]"; - private final ExamRecordMapper examRecordMapper; private final ExamRecordDAO examRecordDAO; private final ApplicationEventPublisher applicationEventPublisher; - private final AdditionalAttributeRecordMapper additionalAttributeRecordMapper; - private final LmsAPIService lmsAPIService; + private final AdditionalAttributesDAO additionalAttributesDAO; public ExamDAOImpl( final ExamRecordMapper examRecordMapper, final ExamRecordDAO examRecordDAO, final ApplicationEventPublisher applicationEventPublisher, - final AdditionalAttributeRecordMapper additionalAttributeRecordMapper, - final LmsAPIService lmsAPIService) { + final AdditionalAttributesDAO additionalAttributesDAO) { this.examRecordMapper = examRecordMapper; this.examRecordDAO = examRecordDAO; this.applicationEventPublisher = applicationEventPublisher; - this.additionalAttributeRecordMapper = additionalAttributeRecordMapper; - this.lmsAPIService = lmsAPIService; + this.additionalAttributesDAO = additionalAttributesDAO; } @Override @@ -103,30 +90,17 @@ public class ExamDAOImpl implements ExamDAO { .flatMap(this::toDomainModel); } - @Override - public Result loadWithAdditionalAttributes(final Long examId) { - return this.examRecordDAO - .recordById(examId) - .flatMap(record -> { - final QuizData quizData = this.lmsAPIService - .getLmsAPITemplate(record.getLmsSetupId()) - .flatMap(template -> template.getQuiz(record.getExternalId())) - .getOrThrow(); - return toDomainModel(record, quizData, null, true); - }); - } - @Override public Result examGrantEntityByPK(final Long id) { return this.examRecordDAO.recordById(id) - .map(record -> toDomainModel(record, null, null).getOrThrow()); + .map(record -> toDomainModel(record).getOrThrow()); } @Override public Result examGrantEntityByClientConnection(final Long connectionId) { return this.examRecordDAO .recordByClientConnection(connectionId) - .map(record -> toDomainModel(record, null, null).getOrThrow()); + .map(record -> toDomainModel(record).getOrThrow()); } @Override @@ -146,34 +120,12 @@ public class ExamDAOImpl implements ExamDAO { return Result.tryCatch(() -> { - final String name = filterMap.getQuizName(); - final DateTime from = filterMap.getExamFromTime(); - final Predicate quizDataFilter = exam -> { - if (StringUtils.isNotBlank(name)) { - if (!exam.name.contains(name)) { - return false; - } - } - - if (from != null && exam.startTime != null) { - // always show exams that has not ended yet - if (exam.endTime == null || exam.endTime.isAfter(from)) { - return true; - } - if (exam.startTime.isBefore(from)) { - return false; - } - } - - return true; - }; - return this.examRecordDAO - .allMatching(filterMap) + .allMatching(filterMap, null) .flatMap(this::toDomainModel) .getOrThrow() .stream() - .filter(quizDataFilter.and(predicate)) + .filter(predicate) .collect(Collectors.toList()); }); } @@ -187,11 +139,37 @@ public class ExamDAOImpl implements ExamDAO { @Override public Result save(final Exam exam) { - return this.examRecordDAO - .save(exam) + return this.checkStateEdit(exam) + .flatMap(this.examRecordDAO::save) + .flatMap(rec -> saveAdditionalAttributes(exam, rec)) .flatMap(this::toDomainModel); } + @Override + public Result updateQuizData( + final Long examId, + final QuizData quizData, + final String updateId) { + + return this.examRecordDAO + .updateFromQuizData(examId, quizData, updateId) + .map(rec -> saveAdditionalQuizAttributes(examId, quizData)); + } + + @Override + public void markLMSAvailability(final String externalQuizId, final boolean available, final String updateId) { + + if (!available) { + log.info("Mark exam quiz data not available from LMS: {}", externalQuizId); + } else { + log.info("Mark exam quiz data back again from LMS: {}", externalQuizId); + } + + this.examRecordDAO.idByExternalQuizId(externalQuizId) + .flatMap(examId -> this.examRecordDAO.updateLmsNotAvailable(examId, available, updateId)) + .onError(error -> log.error("Failed to mark LMS not available: {}", externalQuizId, error)); + } + @Override public Result setSEBRestriction(final Long examId, final boolean sebRestriction) { return this.examRecordDAO @@ -203,6 +181,7 @@ public class ExamDAOImpl implements ExamDAO { public Result createNew(final Exam exam) { return this.examRecordDAO .createNew(exam) + .flatMap(rec -> saveAdditionalAttributes(exam, rec)) .flatMap(this::toDomainModel); } @@ -218,10 +197,11 @@ public class ExamDAOImpl implements ExamDAO { final ExamRecord examRecord = new ExamRecord(null, null, null, null, null, null, null, null, null, null, null, null, null, BooleanUtils.toInteger(active), null, - Utils.getMillisecondsNow()); + Utils.getMillisecondsNow(), null, null, null, null); this.examRecordMapper.updateByExampleSelective(examRecord) .where(ExamRecordDynamicSqlSupport.id, isIn(ids)) + .and(ExamRecordDynamicSqlSupport.status, isNotEqualTo(ExamStatus.ARCHIVED.name())) .build() .execute(); @@ -249,22 +229,26 @@ public class ExamDAOImpl implements ExamDAO { @Override @Transactional(readOnly = true) - public Result> getExamIdsForStatus(final Long institutionId, final ExamStatus status) { - return Result.tryCatch(() -> this.examRecordMapper.selectIdsByExample() - .where( - ExamRecordDynamicSqlSupport.active, - isEqualTo(BooleanUtils.toInteger(true))) - .and( - ExamRecordDynamicSqlSupport.institutionId, - isEqualToWhenPresent(institutionId)) - .and( - ExamRecordDynamicSqlSupport.status, - isEqualTo(status.name())) - .and( - ExamRecordDynamicSqlSupport.updating, - isEqualTo(BooleanUtils.toInteger(false))) - .build() - .execute()); + public Result> getExamsForStatus( + final FilterMap filterMap, + final Predicate predicate, + final ExamStatus... status) { + + return Result.tryCatch(() -> { + + final List stateNames = (status != null && status.length > 0) + ? Arrays.asList(status) + .stream().map(s -> s.name()) + .collect(Collectors.toList()) + : null; + return this.examRecordDAO + .allMatching(filterMap, stateNames) + .flatMap(this::toDomainModel) + .getOrThrow() + .stream() + .filter(predicate) + .collect(Collectors.toList()); + }); } @Override @@ -286,21 +270,33 @@ public class ExamDAOImpl implements ExamDAO { } @Override - public Result> allForRunCheck() { - return this.examRecordDAO - .allForRunCheck() + @Transactional(readOnly = true) + public Result> allForLMSUpdate() { + return Result.tryCatch(() -> this.examRecordMapper.selectByExample() + .where( + ExamRecordDynamicSqlSupport.active, + isEqualTo(BooleanUtils.toInteger(true))) + .and( + ExamRecordDynamicSqlSupport.status, + isNotEqualTo(ExamStatus.ARCHIVED.name())) + .and( + ExamRecordDynamicSqlSupport.updating, + isEqualTo(BooleanUtils.toInteger(false))) + + .build() + .execute()) .flatMap(this::toDomainModel); } @Override - public Result> allForEndCheck() { + public Result> allThatNeedsStatusUpdate(final long leadTime, final long followupTime) { return this.examRecordDAO - .allForEndCheck() + .allThatNeedsStatusUpdate(leadTime, followupTime) .flatMap(this::toDomainModel); } @Override - @Transactional(propagation = Propagation.REQUIRES_NEW) + @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE) public Result placeLock(final Long examId, final String updateId) { return Result.tryCatch(() -> { @@ -311,7 +307,7 @@ public class ExamDAOImpl implements ExamDAO { // consistency check if (BooleanUtils.isTrue(BooleanUtils.toBooleanObject(examRec.getUpdating()))) { throw new IllegalStateException( - "Exam to end update is not in expected state: " + examRec.getExternalId()); + "Exam to place lock is not in expected state: " + examRec.getExternalId()); } final ExamRecord newRecord = new ExamRecord( @@ -319,7 +315,7 @@ public class ExamDAOImpl implements ExamDAO { null, null, null, null, null, null, null, null, null, null, BooleanUtils.toInteger(true), updateId, - null, null, null); + null, null, null, null, null, null, null); this.examRecordMapper.updateByPrimaryKeySelective(newRecord); return examId; @@ -328,7 +324,7 @@ public class ExamDAOImpl implements ExamDAO { } @Override - @Transactional(propagation = Propagation.REQUIRES_NEW) + @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE) public Result releaseLock(final Long examId, final String updateId) { return Result.tryCatch(() -> { @@ -341,7 +337,7 @@ public class ExamDAOImpl implements ExamDAO { || !updateId.equals(examRec.getLastupdate())) { throw new IllegalStateException( - "Exam to end update is not in expected state: " + examRec.getExternalId()); + "Exam to release lock is not in expected state: " + examRec.getExternalId()); } final ExamRecord newRecord = new ExamRecord( @@ -349,7 +345,7 @@ public class ExamDAOImpl implements ExamDAO { null, null, null, null, null, null, null, null, null, null, BooleanUtils.toInteger(false), updateId, - null, null, null); + null, null, null, null, null, null, null); this.examRecordMapper.updateByPrimaryKeySelective(newRecord); return examId; @@ -370,7 +366,7 @@ public class ExamDAOImpl implements ExamDAO { examId, null, null, null, null, null, null, null, null, null, null, BooleanUtils.toInteger(false), - null, null, null, null); + null, null, null, null, null, null, null, null); this.examRecordMapper.updateByPrimaryKeySelective(examRecord); return examRecord.getId(); @@ -494,12 +490,8 @@ public class ExamDAOImpl implements ExamDAO { .execute(); // delete all additional attributes - this.additionalAttributeRecordMapper - .deleteByExample() - .where(AdditionalAttributeRecordDynamicSqlSupport.entityType, isEqualTo(EntityType.EXAM.name())) - .and(AdditionalAttributeRecordDynamicSqlSupport.entityId, isIn(ids)) - .build() - .execute(); + ids.stream() + .forEach(id -> this.additionalAttributesDAO.deleteAll(EntityType.EXAM, id)); return ids.stream() .map(id -> new EntityKey(id, EntityType.EXAM)) @@ -592,7 +584,11 @@ public class ExamDAOImpl implements ExamDAO { rec.getLastupdate(), rec.getActive(), null, - Utils.getMillisecondsNow())); + Utils.getMillisecondsNow(), + rec.getQuizName(), + rec.getQuizStartTime(), + rec.getQuizEndTime(), + rec.getLmsAvailable())); result.add(new EntityKey(rec.getId(), EntityType.EXAM)); } catch (final Exception e) { @@ -617,6 +613,9 @@ public class ExamDAOImpl implements ExamDAO { .and( ExamRecordDynamicSqlSupport.status, isEqualTo(ExamStatus.RUNNING.name())) + .and( + ExamRecordDynamicSqlSupport.lmsAvailable, + isEqualToWhenPresent(BooleanUtils.toIntegerObject(true))) .build() .execute()); } @@ -671,205 +670,15 @@ public class ExamDAOImpl implements ExamDAO { exam.getDescription()); } - private Result toDomainModel(final ExamRecord record) { - return toDomainModel( - record.getLmsSetupId(), - Arrays.asList(record)) - .map(col -> col.iterator().next()); - } - private Result> toDomainModel(final Collection records) { - return Result.tryCatch(() -> { - - final HashMap> lmsSetupToRecordMapping = records - .stream() - .reduce(new LinkedHashMap<>(), - (map, record) -> Utils.mapCollect(map, record.getLmsSetupId(), record), - Utils::mapPutAll); - - return lmsSetupToRecordMapping - .entrySet() - .stream() - .flatMap(entry -> toDomainModel( - entry.getKey(), - entry.getValue()) - .onError(error -> log.error( - "Failed to get quizzes from LMS Setup: {}", - entry.getKey(), error)) - .getOr(Collections.emptyList()) - .stream()) + return records.stream() + .map(rec -> this.toDomainModel(rec).getOrThrow()) .collect(Collectors.toList()); }); } - private Result> toDomainModel( - final Long lmsSetupId, - final Collection records) { - - return Result.tryCatch(() -> { - - // map records - final Map recordMapping = records - .stream() - .collect(Collectors.toMap(ExamRecord::getExternalId, Function.identity())); - - // get and map quizzes - final Map quizzes = this.lmsAPIService - .getLmsAPITemplate(lmsSetupId) - .flatMap(template -> template.getQuizzes(recordMapping.keySet())) - .getOrElse(() -> Collections.emptyList()) - .stream() - .collect(Collectors.toMap(q -> q.id, Function.identity())); - - if (records.size() != quizzes.size()) { - - // Check if we have LMS connection to verify the source of the exam quiz mismatch - final LmsAPITemplate lmsSetup = this.lmsAPIService - .getLmsAPITemplate(lmsSetupId) - .getOrThrow(); - - if (log.isDebugEnabled()) { - log.debug("Quizzes size mismatch detected by getting exams quiz data from LMS: {}", lmsSetup); - } - - if (lmsSetup.testCourseAccessAPI().hasAnyError()) { - // No course access on the LMS. This means we can't get any quizzes from this LMSSetup at the moment - // All exams are marked as corrupt because of LMS Setup failure - - log.warn("Failed to get quizzes form LMS Setup. No access to LMS {}", lmsSetup.lmsSetup()); - - return recordMapping.entrySet() - .stream() - .map(entry -> toDomainModel( - entry.getValue(), - null, - ExamStatus.CORRUPT_NO_LMS_CONNECTION) - .getOr(null)) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - } - } - - // collect Exam's - return recordMapping.entrySet() - .stream() - .map(entry -> toDomainModel( - entry.getValue(), - getQuizData(quizzes, entry.getKey(), entry.getValue()), - ExamStatus.CORRUPT_INVALID_ID) - .onError(error -> log.error( - "Failed to get quiz data from remote LMS for exam: ", - error)) - .getOr(null)) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - }); - } - - private QuizData getQuizData( - final Map quizzes, - final String externalId, - final ExamRecord record) { - - if (quizzes.containsKey(externalId)) { - return quizzes.get(externalId); - } else { - // If this is a Moodle quiz, try to recover from eventually restore of the quiz on the LMS side - // NOTE: This is a workaround for Moodle quizzes that had have a recovery within the sandbox tool - // Where potentially quiz identifiers get changed during such a recovery and the SEB Server - // internal mapping is not working properly anymore. In this case we try to recover from such - // a case by using the short name of the quiz and search for the quiz within the course with this - // short name. If one quiz has been found that matches all criteria, we adapt the internal id - // mapping to this quiz. - // If recovering fails, this returns null and the calling side must handle the lack of quiz data - try { - final LmsSetup lmsSetup = this.lmsAPIService - .getLmsSetup(record.getLmsSetupId()) - .getOrThrow(); - if (lmsSetup.lmsType == LmsType.MOODLE) { - - log.info("Try to recover quiz data for Moodle quiz with internal identifier: {}", externalId); - - // get additional quiz name attribute - final AdditionalAttributeRecord additionalAttribute = - this.additionalAttributeRecordMapper.selectByExample() - .where( - AdditionalAttributeRecordDynamicSqlSupport.entityType, - SqlBuilder.isEqualTo(EntityType.EXAM.name())) - .and( - AdditionalAttributeRecordDynamicSqlSupport.entityId, - SqlBuilder.isEqualTo(record.getId())) - .and( - AdditionalAttributeRecordDynamicSqlSupport.name, - SqlBuilder.isEqualTo(QuizData.QUIZ_ATTR_NAME)) - .build() - .execute() - .stream() - .findAny() - .orElse(null); - if (additionalAttribute != null) { - - log.debug("Found additional quiz name attribute: {}", additionalAttribute); - - // get the course name identifier - final String shortname = MoodleCourseAccess.getShortname(externalId); - if (StringUtils.isNotBlank(shortname)) { - - log.debug("Using short-name: {} for recovering", shortname); - - final QuizData recoveredQuizData = this.lmsAPIService - .getLmsAPITemplate(lmsSetup.id) - .map(template -> template.getQuizzes(new FilterMap()) - .getOrThrow() - .stream() - .filter(quiz -> { - final String qShortName = MoodleCourseAccess.getShortname(quiz.id); - return qShortName != null && qShortName.equals(shortname); - }) - .filter(quiz -> additionalAttribute.getValue().equals(quiz.name)) - .findAny() - .get()) - .getOrThrow(); - if (recoveredQuizData != null) { - - log.debug("Found quiz data for recovering: {}", recoveredQuizData); - - // save exam with new external id - this.examRecordMapper.updateByPrimaryKeySelective(new ExamRecord( - record.getId(), - null, null, - recoveredQuizData.id, - null, null, null, null, null, null, null, null, null, null, null, - Utils.getMillisecondsNow())); - - log.debug("Successfully recovered exam quiz data to new externalId {}", - recoveredQuizData.id); - } - return recoveredQuizData; - } - } - } - } catch (final Exception e) { - log.warn("Failed to try to recover from Moodle quiz restore: {}", e.getMessage()); - } - return null; - } - } - - private Result toDomainModel( - final ExamRecord record, - final QuizData quizData, - final ExamStatus statusOverride) { - - return this.toDomainModel(record, quizData, statusOverride, false); - } - - private Result toDomainModel( - final ExamRecord record, - final QuizData quizData, - final ExamStatus statusOverride, - final boolean withAdditionalAttributed) { + private Result toDomainModel(final ExamRecord record) { return Result.tryCatch(() -> { @@ -885,38 +694,30 @@ public class ExamDAOImpl implements ExamDAO { status = ExamStatus.UP_COMING; } - Map additionalAttributes = null; - if (withAdditionalAttributed) { - additionalAttributes = this.additionalAttributeRecordMapper.selectByExample() - .where( - AdditionalAttributeRecordDynamicSqlSupport.entityType, - SqlBuilder.isEqualTo(EntityType.EXAM.name())) - .and( - AdditionalAttributeRecordDynamicSqlSupport.entityId, - SqlBuilder.isEqualTo(record.getId())) - .build() - .execute() - .stream() - .collect(Collectors.toMap(r -> r.getName(), r -> r.getValue())); - } + final Map additionalAttributes = this.additionalAttributesDAO + .getAdditionalAttributes(EntityType.EXAM, record.getId()) + .getOrThrow() + .stream() + .collect(Collectors.toMap(rec -> rec.getName(), rec -> rec.getValue())); return new Exam( record.getId(), record.getInstitutionId(), record.getLmsSetupId(), record.getExternalId(), - (quizData != null) ? quizData.name : FAILED_TO_LOAD_QUIZ_DATA_MARK, - (quizData != null) ? quizData.description : FAILED_TO_LOAD_QUIZ_DATA_MARK, - (quizData != null) ? quizData.startTime : new DateTime(0), - (quizData != null) ? quizData.endTime : null, - (quizData != null) ? quizData.startURL : Constants.EMPTY_NOTE, + BooleanUtils.toBooleanObject(record.getLmsAvailable()), + StringUtils.isNotBlank(record.getQuizName()) + ? record.getQuizName() + : Constants.SQUARE_BRACE_OPEN + record.getExternalId() + Constants.SQUARE_BRACE_CLOSE, + record.getQuizStartTime(), + record.getQuizEndTime(), ExamType.valueOf(record.getType()), record.getOwner(), supporter, - (quizData != null) ? status : (statusOverride != null) ? statusOverride : status, + status, BooleanUtils.toBooleanObject(record.getLmsSebRestriction()), record.getBrowserKeys(), - BooleanUtils.toBooleanObject((quizData != null) ? record.getActive() : null), + BooleanUtils.toBooleanObject(record.getActive()), record.getLastupdate(), record.getExamTemplateId(), record.getLastModified(), @@ -924,4 +725,44 @@ public class ExamDAOImpl implements ExamDAO { }); } + private Result saveAdditionalAttributes(final Exam exam, final ExamRecord rec) { + return Result.tryCatch(() -> { + if (exam.additionalAttributesIncluded()) { + this.additionalAttributesDAO.saveAdditionalAttributes( + EntityType.EXAM, + rec.getId(), + exam.additionalAttributes) + .getOrThrow(); + } + + return rec; + + }); + } + + private QuizData saveAdditionalQuizAttributes(final Long examId, final QuizData quizData) { + final Map additionalAttributes = new HashMap<>(quizData.getAdditionalAttributes()); + additionalAttributes.put(QuizData.QUIZ_ATTR_DESCRIPTION, quizData.description); + additionalAttributes.put(QuizData.QUIZ_ATTR_START_URL, quizData.startURL); + + this.additionalAttributesDAO.saveAdditionalAttributes( + EntityType.EXAM, + examId, + additionalAttributes) + .getOrThrow(); + + return quizData; + } + + private Result checkStateEdit(final Exam exam) { + return Result.tryCatch(() -> { + + if (exam.status == ExamStatus.ARCHIVED) { + throw new APIMessageException(APIMessage.ErrorMessage.INTEGRITY_VALIDATION.of("Exam is archived")); + } + + return exam; + }); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamRecordDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamRecordDAO.java index e02aa7eb..3b791e0a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamRecordDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamRecordDAO.java @@ -19,18 +19,25 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; import org.mybatis.dynamic.sql.SqlBuilder; +import org.mybatis.dynamic.sql.SqlCriterion; import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter; import org.mybatis.dynamic.sql.select.QueryExpressionDSL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; +import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Utils; @@ -50,6 +57,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler; @WebServiceProfile public class ExamRecordDAO { + private static final Logger log = LoggerFactory.getLogger(ExamRecordDAO.class); + private final ExamRecordMapper examRecordMapper; private final ClientConnectionRecordMapper clientConnectionRecordMapper; @@ -74,6 +83,20 @@ public class ExamRecordDAO { }); } + @Transactional(readOnly = true) + public Result idByExternalQuizId(final String externalQuizId) { + return Result.tryCatch(() -> { + return this.examRecordMapper.selectIdsByExample() + .where( + ExamRecordDynamicSqlSupport.externalId, + isEqualToWhenPresent(externalQuizId)) + .build() + .execute() + .stream() + .collect(Utils.toSingleton()); + }); + } + @Transactional(readOnly = true) public Result recordByClientConnection(final Long connectionId) { return Result.tryCatch(() -> this.clientConnectionRecordMapper @@ -117,13 +140,13 @@ public class ExamRecordDAO { } @Transactional(readOnly = true) - public Result> allMatching(final FilterMap filterMap) { + public Result> allMatching(final FilterMap filterMap, final List stateNames) { return Result.tryCatch(() -> { // If we have a sort on institution name, join the institution table // If we have a sort on lms setup name, join lms setup table - final QueryExpressionDSL>>.QueryExpressionWhereBuilder whereClause = + QueryExpressionDSL>>.QueryExpressionWhereBuilder whereClause = (filterMap.getBoolean(FilterMap.ATTR_ADD_INSITUTION_JOIN)) ? this.examRecordMapper .selectByExample() @@ -149,7 +172,7 @@ public class ExamRecordDAO { ExamRecordDynamicSqlSupport.active, isEqualToWhenPresent(filterMap.getActiveAsInt())); - final List records = whereClause + whereClause = whereClause .and( ExamRecordDynamicSqlSupport.institutionId, isEqualToWhenPresent(filterMap.getInstitutionId())) @@ -158,10 +181,39 @@ public class ExamRecordDAO { isEqualToWhenPresent(filterMap.getLmsSetupId())) .and( ExamRecordDynamicSqlSupport.type, - isEqualToWhenPresent(filterMap.getExamType())) + isEqualToWhenPresent(filterMap.getExamType())); + + final String examStatus = filterMap.getExamStatus(); + if (StringUtils.isNotBlank(examStatus)) { + whereClause = whereClause + .and( + ExamRecordDynamicSqlSupport.status, + isEqualToWhenPresent(examStatus)); + } else if (stateNames != null && !stateNames.isEmpty()) { + whereClause = whereClause + .and( + ExamRecordDynamicSqlSupport.status, + isInWhenPresent(stateNames)); + } else { + // for default the archived state is not presented only on explicit request + whereClause = whereClause + .and( + ExamRecordDynamicSqlSupport.status, + isNotEqualTo(ExamStatus.ARCHIVED.name())); + } + + if (filterMap.getExamFromTime() != null) { + whereClause = whereClause + .and( + ExamRecordDynamicSqlSupport.quizEndTime, + isGreaterThanOrEqualToWhenPresent(filterMap.getExamFromTime()), + or(ExamRecordDynamicSqlSupport.quizEndTime, isNull())); + } + + final List records = whereClause .and( - ExamRecordDynamicSqlSupport.status, - isEqualToWhenPresent(filterMap.getExamStatus())) + ExamRecordDynamicSqlSupport.quizName, + isLikeWhenPresent(filterMap.getSQLWildcard(EXAM.ATTR_QUIZ_NAME))) .build() .execute(); @@ -173,7 +225,9 @@ public class ExamRecordDAO { public Result updateState(final Long examId, final ExamStatus status, final String updateId) { return recordById(examId) .map(examRecord -> { - if (BooleanUtils.isTrue(BooleanUtils.toBooleanObject(examRecord.getUpdating()))) { + if (updateId != null && + BooleanUtils.isTrue(BooleanUtils.toBooleanObject(examRecord.getUpdating()))) { + if (!updateId.equals(examRecord.getLastupdate())) { throw new IllegalStateException("Exam is currently locked: " + examRecord.getExternalId()); } @@ -184,7 +238,7 @@ public class ExamRecordDAO { null, null, null, null, null, null, null, null, status.name(), null, null, null, null, null, - Utils.getMillisecondsNow()); + Utils.getMillisecondsNow(), null, null, null, null); this.examRecordMapper.updateByPrimaryKeySelective(newExamRecord); return this.examRecordMapper.selectByPrimaryKey(examId); @@ -202,6 +256,13 @@ public class ExamRecordDAO { throw new IllegalStateException("Exam is currently locked: " + exam.externalId); } + if (exam.status != null && !exam.status.name().equals(oldRecord.getStatus())) { + log.warn("Exam state change on save. Exam. {}, Old state: {}, new state: {}", + exam.externalId, + oldRecord.getStatus(), + exam.status); + } + final ExamRecord examRecord = new ExamRecord( exam.id, null, null, null, null, @@ -213,15 +274,14 @@ public class ExamRecordDAO { : null, null, exam.browserExamKeys, - (exam.status != null) - ? exam.status.name() - : null, + null, 1, // seb restriction (deprecated) null, // updating null, // lastUpdate null, // active exam.examTemplateId, - Utils.getMillisecondsNow()); + Utils.getMillisecondsNow(), + null, null, null, null); this.examRecordMapper.updateByPrimaryKeySelective(examRecord); return this.examRecordMapper.selectByPrimaryKey(exam.id); @@ -229,6 +289,90 @@ public class ExamRecordDAO { .onError(TransactionHandler::rollback); } + @Transactional + public Result updateFromQuizData( + final Long examId, + final QuizData quizData, + final String updateId) { + + return Result.tryCatch(() -> { + + // check internal persistent write-lock + final ExamRecord oldRecord = this.examRecordMapper.selectByPrimaryKey(examId); + if (BooleanUtils.isTrue(BooleanUtils.toBooleanObject(oldRecord.getUpdating()))) { + throw new IllegalStateException("Exam is currently locked: " + examId); + } + + final ExamRecord examRecord = new ExamRecord( + examId, + null, null, + quizData.id, + null, null, null, null, null, null, null, null, + updateId, + null, null, + Utils.getMillisecondsNow(), + quizData.getName(), + quizData.getStartTime(), + quizData.getEndTime(), + BooleanUtils.toIntegerObject(true)); + + this.examRecordMapper.updateByPrimaryKeySelective(examRecord); + return this.examRecordMapper.selectByPrimaryKey(examId); + }) + .onError(TransactionHandler::rollback); + } + + @Transactional + public Result updateLmsNotAvailable(final Long examId, final boolean available, final String updateId) { + return Result.tryCatch(() -> { + + // check internal persistent write-lock + final ExamRecord oldRecord = this.examRecordMapper.selectByPrimaryKey(examId); + if (BooleanUtils.isTrue(BooleanUtils.toBooleanObject(oldRecord.getUpdating()))) { + throw new IllegalStateException("Exam is currently locked: " + examId); + } + + final ExamRecord examRecord = new ExamRecord( + examId, + null, null, null, null, null, null, null, null, null, + null, null, + updateId, + null, null, + Utils.getMillisecondsNow(), + null, null, null, + BooleanUtils.toIntegerObject(available)); + + this.examRecordMapper.updateByPrimaryKeySelective(examRecord); + return this.examRecordMapper.selectByPrimaryKey(examId); + }) + .onError(TransactionHandler::rollback); + } + + @Transactional + public Result archive(final Long examId) { + return Result.tryCatch(() -> { + + // check internal persistent write-lock + final ExamRecord oldRecord = this.examRecordMapper.selectByPrimaryKey(examId); + if (BooleanUtils.isTrue(BooleanUtils.toBooleanObject(oldRecord.getUpdating()))) { + throw new IllegalStateException("Exam is currently locked: " + examId); + } + + final ExamRecord examRecord = new ExamRecord( + examId, + null, null, null, null, null, null, null, null, + ExamStatus.ARCHIVED.name(), + null, null, null, null, null, + Utils.getMillisecondsNow(), + null, null, null, + BooleanUtils.toIntegerObject(false)); + + this.examRecordMapper.updateByPrimaryKeySelective(examRecord); + return this.examRecordMapper.selectByPrimaryKey(examId); + }) + .onError(TransactionHandler::rollback); + } + @Transactional public Result setSEBRestriction(final Long examId, final boolean sebRestriction) { return Result.tryCatch(() -> { @@ -238,7 +382,8 @@ public class ExamRecordDAO { null, null, null, null, null, null, null, null, null, BooleanUtils.toInteger(sebRestriction), null, null, null, null, - Utils.getMillisecondsNow()); + Utils.getMillisecondsNow(), + null, null, null, null); this.examRecordMapper.updateByPrimaryKeySelective(examRecord); return this.examRecordMapper.selectByPrimaryKey(examId); @@ -285,7 +430,11 @@ public class ExamRecordDAO { null, // lastUpdate BooleanUtils.toInteger(true), exam.examTemplateId, - Utils.getMillisecondsNow()); + Utils.getMillisecondsNow(), + exam.name, + exam.startTime, + exam.endTime, + BooleanUtils.toIntegerObject(true)); this.examRecordMapper.insert(examRecord); return examRecord; @@ -294,27 +443,14 @@ public class ExamRecordDAO { } @Transactional(readOnly = true) - public Result> allForRunCheck() { + public Result> allThatNeedsStatusUpdate(final long leadTime, final long followupTime) { return Result.tryCatch(() -> { - return this.examRecordMapper.selectByExample() - .where( - ExamRecordDynamicSqlSupport.active, - isEqualTo(BooleanUtils.toInteger(true))) - .and( - ExamRecordDynamicSqlSupport.status, - isNotEqualTo(ExamStatus.RUNNING.name())) - .and( - ExamRecordDynamicSqlSupport.updating, - isEqualTo(BooleanUtils.toInteger(false))) - .build() - .execute(); - }); - } - @Transactional(readOnly = true) - public Result> allForEndCheck() { - return Result.tryCatch(() -> { - return this.examRecordMapper.selectByExample() + final DateTime now = DateTime.now(DateTimeZone.UTC); + final List result = new ArrayList<>(); + + // check those on running state that are not within the time-frame anymore + final List running = this.examRecordMapper.selectByExample() .where( ExamRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(true))) @@ -324,8 +460,59 @@ public class ExamRecordDAO { .and( ExamRecordDynamicSqlSupport.updating, isEqualTo(BooleanUtils.toInteger(false))) + .and( // not within time frame + ExamRecordDynamicSqlSupport.quizStartTime, + SqlBuilder.isGreaterThanOrEqualToWhenPresent(now.plus(leadTime)), + or( + ExamRecordDynamicSqlSupport.quizEndTime, + SqlBuilder.isLessThanWhenPresent(now.minus(followupTime)))) .build() .execute(); + + // check those in not running state (and not archived) and are within the time-frame or on wrong side of the time-frame + // if finished but up-coming or running + final SqlCriterion finished = or( + ExamRecordDynamicSqlSupport.status, + isEqualTo(ExamStatus.FINISHED.name()), + and( + ExamRecordDynamicSqlSupport.quizEndTime, + SqlBuilder.isGreaterThanOrEqualToWhenPresent(now.plus(leadTime)))); + + // if up-coming but running or finished + final SqlCriterion upcoming = or( + ExamRecordDynamicSqlSupport.status, + isEqualTo(ExamStatus.UP_COMING.name()), + and( + ExamRecordDynamicSqlSupport.quizStartTime, + SqlBuilder.isLessThanWhenPresent(now.minus(followupTime))), + finished); + + final List notRunning = this.examRecordMapper.selectByExample() + .where( + ExamRecordDynamicSqlSupport.active, + isEqualTo(BooleanUtils.toInteger(true))) + .and( + ExamRecordDynamicSqlSupport.status, + isNotEqualTo(ExamStatus.RUNNING.name())) + .and( + ExamRecordDynamicSqlSupport.status, + isNotEqualTo(ExamStatus.ARCHIVED.name())) + .and( + ExamRecordDynamicSqlSupport.updating, + isEqualTo(BooleanUtils.toInteger(false))) + .and( // within time frame + ExamRecordDynamicSqlSupport.quizStartTime, + SqlBuilder.isLessThanWhenPresent(now.plus(leadTime)), + and( + ExamRecordDynamicSqlSupport.quizEndTime, + SqlBuilder.isGreaterThanOrEqualToWhenPresent(now.minus(followupTime))), + upcoming) + .build() + .execute(); + + result.addAll(running); + result.addAll(notRunning); + return result; }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java index a5108e47..240ad1ac 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamTemplateDAOImpl.java @@ -110,10 +110,6 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO { throw new ResourceNotFoundException(EntityType.EXAM_TEMPLATE, String.valueOf(institutionId)); } - if (defaults.size() != 1) { - throw new IllegalStateException("Expected one default but was: " + defaults.size()); - } - return defaults.get(0); }) .flatMap(this::toDomainModel); @@ -185,6 +181,7 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO { return Result.tryCatch(() -> { checkUniqueName(data); + checkUniqueDefault(data); final Collection indicatorTemplates = data.getIndicatorTemplates(); final String indicatorsJSON = (indicatorTemplates != null && !indicatorTemplates.isEmpty()) @@ -388,6 +385,11 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO { @Override public Set getDependencies(final BulkAction bulkAction) { + // all of institution + if (bulkAction.sourceType == EntityType.INSTITUTION) { + return getDependencies(bulkAction, this::allIdsOfInstitution); + } + return Collections.emptySet(); } @@ -534,11 +536,13 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO { } } - private void checkUniqueIndicatorName(final IndicatorTemplate indicatorTemplate, + private void checkUniqueIndicatorName( + final IndicatorTemplate indicatorTemplate, final Collection indicators) { + // check unique name indicators.stream() - .filter(it -> Objects.equals(it.name, indicatorTemplate.name)) + .filter(it -> !Objects.equals(it, indicatorTemplate) && Objects.equals(it.name, indicatorTemplate.name)) .findAny() .ifPresent(it -> { throw new FieldValidationException( @@ -554,4 +558,19 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO { .orElse(-1L) + 1; } + private Result> allIdsOfInstitution(final EntityKey institutionKey) { + return Result.tryCatch(() -> this.examTemplateRecordMapper.selectByExample() + .where(ExamTemplateRecordDynamicSqlSupport.institutionId, + isEqualTo(Long.valueOf(institutionKey.modelId))) + .build() + .execute() + .stream() + .map(rec -> new EntityDependency( + institutionKey, + new EntityKey(rec.getId(), EntityType.EXAM_TEMPLATE), + rec.getName(), + rec.getDescription())) + .collect(Collectors.toList())); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java index 4a69787c..8f11855d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java @@ -159,7 +159,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO { } return lmsSetup.updateTime.equals(record.getUpdateTime()); } catch (final Exception e) { - log.error("Failed to check snyc on LmsSetup: {}", lmsSetup); + log.error("Failed to check sync on LmsSetup: {}", lmsSetup); return false; } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SEBClientConfigDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SEBClientConfigDAOImpl.java index 3ef769f6..6b19a497 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SEBClientConfigDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SEBClientConfigDAOImpl.java @@ -24,6 +24,8 @@ import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; +import org.springframework.cache.CacheManager; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -50,10 +52,13 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttribut import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.SebClientConfigRecord; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DAOLoggingSupport; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DAOUserServcie; 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.SEBClientConfigDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ClientConfigService; +import ch.ethz.seb.sebserver.webservice.weblayer.oauth.RevokeTokenEndpoint.RevokeExamTokenEvent; @Lazy @Component @@ -63,15 +68,24 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO { private final SebClientConfigRecordMapper sebClientConfigRecordMapper; private final ClientCredentialService clientCredentialService; private final AdditionalAttributesDAOImpl additionalAttributesDAO; + private final DAOUserServcie daoUserServcie; + private final ApplicationEventPublisher applicationEventPublisher; + private final CacheManager cacheManager; protected SEBClientConfigDAOImpl( final SebClientConfigRecordMapper sebClientConfigRecordMapper, final ClientCredentialService clientCredentialService, - final AdditionalAttributesDAOImpl additionalAttributesDAO) { + final AdditionalAttributesDAOImpl additionalAttributesDAO, + final DAOUserServcie daoUserServcie, + final ApplicationEventPublisher applicationEventPublisher, + final CacheManager cacheManager) { this.sebClientConfigRecordMapper = sebClientConfigRecordMapper; this.clientCredentialService = clientCredentialService; this.additionalAttributesDAO = additionalAttributesDAO; + this.daoUserServcie = daoUserServcie; + this.applicationEventPublisher = applicationEventPublisher; + this.cacheManager = cacheManager; } @Override @@ -140,6 +154,7 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO { } @Override + @Transactional(readOnly = true) public Result byClientName(final String clientName) { return Result.tryCatch(() -> this.sebClientConfigRecordMapper .selectByExample() @@ -200,9 +215,14 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO { return Collections.emptyList(); } + // clear caches and revoke tokens first + ids.stream().forEach(this::disposeSEBClientConfig); + final SebClientConfigRecord record = new SebClientConfigRecord( null, null, null, null, null, null, null, - BooleanUtils.toIntegerObject(active)); + BooleanUtils.toIntegerObject(active), + Utils.getMillisecondsNow(), + this.daoUserServcie.getCurrentUserUUID()); this.sebClientConfigRecordMapper.updateByExampleSelective(record) .where(SebClientConfigRecordDynamicSqlSupport.id, isIn(ids)) @@ -232,7 +252,9 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO { cc.clientIdAsString(), cc.secretAsString(), getEncryptionPassword(sebClientConfig), - BooleanUtils.toInteger(BooleanUtils.isTrue(sebClientConfig.active))); + BooleanUtils.toInteger(BooleanUtils.isTrue(sebClientConfig.active)), + Utils.getMillisecondsNow(), + this.daoUserServcie.getCurrentUserUUID()); this.sebClientConfigRecordMapper .insert(newRecord); @@ -263,7 +285,9 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO { record.getClientName(), record.getClientSecret(), getEncryptionPassword(sebClientConfig), - record.getActive()); + record.getActive(), + Utils.getMillisecondsNow(), + this.daoUserServcie.getCurrentUserUUID()); this.sebClientConfigRecordMapper.updateByPrimaryKey(newRecord); @@ -286,6 +310,9 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO { return Collections.emptyList(); } + // clear caches and revoke tokens first + ids.stream().forEach(this::disposeSEBClientConfig); + this.sebClientConfigRecordMapper.deleteByExample() .where(SebClientConfigRecordDynamicSqlSupport.id, isIn(ids)) .build() @@ -447,7 +474,9 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO { ? additionalAttributes.get(SEBClientConfig.ATTR_ENCRYPT_CERTIFICATE_ALIAS).getValue() : null, additionalAttributes.containsKey(SEBClientConfig.ATTR_ENCRYPT_CERTIFICATE_ASYM), - BooleanUtils.toBooleanObject(record.getActive()))); + BooleanUtils.toBooleanObject(record.getActive()), + Utils.toDateTimeUTC(record.getLastUpdateTime()), + record.getLastUpdateUser())); } private String getEncryptionPassword(final SEBClientConfig sebClientConfig) { @@ -645,4 +674,29 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO { } } + private Long disposeSEBClientConfig(final Long pk) { + + try { + final SebClientConfigRecord rec = recordById(pk) + .getOrThrow(); + + // revoke token + try { + this.applicationEventPublisher + .publishEvent(new RevokeExamTokenEvent(rec.getClientName())); + } catch (final Exception e) { + log.error("Failed to revoke token for SEB client connection. Connection Configuration: {}", pk, e); + } + + // clear cache + this.cacheManager + .getCache(ClientConfigService.EXAM_CLIENT_DETAILS_CACHE) + .evictIfPresent(rec.getClientName()); + + } catch (final Exception e) { + log.error("Failed to revoke SEB client connection. Connection Configuration: {}", pk, e); + } + + return pk; + } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java index dabfeda0..b22b9e3a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java @@ -160,6 +160,11 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO { return log(UserLogActivityType.MODIFY, entity); } + @Override + public Result logFinished(final E entity) { + return log(UserLogActivityType.FINISHED, entity); + } + @Override @Transactional public Result logDelete(final E entity) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java index 24ce95e2..1ed5da11 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java @@ -8,11 +8,19 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.exam; -import org.apache.commons.lang3.BooleanUtils; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.commons.lang3.BooleanUtils; +import org.springframework.validation.FieldError; + +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; +import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; -import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService; @@ -83,19 +91,52 @@ public interface ExamAdminService { * @return Result refer to proctoring is enabled flag or to an error when happened. */ Result isProctoringEnabled(final Long examId); - /** Get the exam proctoring service implementation of specified type. - * - * @param type exam proctoring service server type - * @return ExamProctoringService instance */ - Result getExamProctoringService(final ProctoringServerType type); - /** Get the exam proctoring service implementation for specified exam. * * @param examId the exam identifier * @return ExamProctoringService instance */ - default Result getExamProctoringService(final Long examId) { - return getProctoringServiceSettings(examId) - .flatMap(settings -> getExamProctoringService(settings.serverType)); + Result getExamProctoringService(final Long examId); + + /** This archives a finished exam and set it to archived state as well as the assigned + * exam configurations that are also set to archived state. + * + * @param exam The exam to archive + * @return Result refer to the archived exam or to an error when happened */ + Result archiveExam(Exam exam); + + /** Used to check threshold consistency for a given list of thresholds. + * Checks if all values are present (none null value) + * Checks if there are duplicates + * + * If a check fails, the methods throws a APIMessageException with a FieldError to notify the caller + * + * @param thresholds List of Threshold */ + public static void checkThresholdConsistency(final List thresholds) { + if (thresholds != null) { + final List emptyThresholds = thresholds.stream() + .filter(t -> t.getValue() == null || t.getColor() == null) + .collect(Collectors.toList()); + + if (!emptyThresholds.isEmpty()) { + throw new APIMessageException(APIMessage.fieldValidationError( + new FieldError( + Domain.EXAM.TYPE_NAME, + Domain.EXAM.ATTR_SUPPORTER, + "indicator:thresholds:thresholdEmpty"))); + } + + final Set values = thresholds.stream() + .map(t -> t.getValue()) + .collect(Collectors.toSet()); + + if (values.size() != thresholds.size()) { + throw new APIMessageException(APIMessage.fieldValidationError( + new FieldError( + Domain.EXAM.TYPE_NAME, + Domain.EXAM.ATTR_SUPPORTER, + "indicator:thresholds:thresholdDuplicate"))); + } + } } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ProctoringAdminService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ProctoringAdminService.java new file mode 100644 index 00000000..6a0d3355 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ProctoringAdminService.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.exam; + +import java.util.EnumSet; + +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; +import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType; +import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService; + +public interface ProctoringAdminService { + + EnumSet SUPPORTED_PARENT_ENTITES = EnumSet.of( + EntityType.EXAM, + EntityType.EXAM_TEMPLATE); + + /** Get proctoring service settings for a certain entity (SUPPORTED_PARENT_ENTITES). + * + * @param parentEntityKey the entity key of the parent entity to get the proctoring service settings from + * @return Result refer to proctoring service settings or to an error when happened. */ + Result getProctoringSettings(EntityKey parentEntityKey); + + /** Save the given proctoring service settings for a certain entity (SUPPORTED_PARENT_ENTITES). + * + * @param parentEntityKey the entity key of the parent entity to save the proctoring service settings to + * @param proctoringServiceSettings The proctoring service settings to save + * @return Result refer to saved proctoring service settings or to an error when happened. */ + Result saveProctoringServiceSettings( + EntityKey parentEntityKey, + ProctoringServiceSettings proctoringServiceSettings); + + /** Get the exam proctoring service implementation of specified type. + * + * @param type exam proctoring service server type + * @return ExamProctoringService instance */ + Result getExamProctoringService(final ProctoringServerType type); + + /** Use this to test the proctoring service settings against the remote proctoring server. + * + * @param proctoringSettings the settings to test + * @return Result refer to true if the settings are correct and the proctoring server can be accessed. */ + default Result testExamProctoring(final ProctoringServiceSettings proctoringSettings) { + return getExamProctoringService(proctoringSettings.serverType) + .flatMap(service -> service.testExamProctoring(proctoringSettings)); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java index 2d38f963..b424bbe2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java @@ -9,12 +9,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.exam.impl; import java.util.Arrays; -import java.util.EnumSet; import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.Function; -import java.util.stream.Collectors; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; @@ -25,29 +20,31 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSEBRestriction; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; -import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature; -import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType; 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.LmsSetup.LmsType; +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.profile.WebServiceProfile; -import ch.ethz.seb.sebserver.gbl.util.Cryptor; import ch.ethz.seb.sebserver.gbl.util.Result; -import ch.ethz.seb.sebserver.gbl.util.Utils; -import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; -import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService; +import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService; -import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.ExamProctoringServiceFactory; @Lazy @Service @@ -57,26 +54,26 @@ public class ExamAdminServiceImpl implements ExamAdminService { private static final Logger log = LoggerFactory.getLogger(ExamAdminServiceImpl.class); private final ExamDAO examDAO; + private final ProctoringAdminService proctoringServiceSettingsService; private final AdditionalAttributesDAO additionalAttributesDAO; + private final ConfigurationNodeDAO configurationNodeDAO; + private final ExamConfigurationMapDAO examConfigurationMapDAO; private final LmsAPIService lmsAPIService; - private final Cryptor cryptor; - private final ExamProctoringServiceFactory examProctoringServiceFactory; - private final RemoteProctoringRoomDAO remoteProctoringRoomDAO; protected ExamAdminServiceImpl( final ExamDAO examDAO, + final ProctoringAdminService proctoringServiceSettingsService, final AdditionalAttributesDAO additionalAttributesDAO, - final LmsAPIService lmsAPIService, - final Cryptor cryptor, - final ExamProctoringServiceFactory examProctoringServiceFactory, - final RemoteProctoringRoomDAO remoteProctoringRoomDAO) { + final ConfigurationNodeDAO configurationNodeDAO, + final ExamConfigurationMapDAO examConfigurationMapDAO, + final LmsAPIService lmsAPIService) { this.examDAO = examDAO; + this.proctoringServiceSettingsService = proctoringServiceSettingsService; this.additionalAttributesDAO = additionalAttributesDAO; + this.configurationNodeDAO = configurationNodeDAO; + this.examConfigurationMapDAO = examConfigurationMapDAO; this.lmsAPIService = lmsAPIService; - this.cryptor = cryptor; - this.examProctoringServiceFactory = examProctoringServiceFactory; - this.remoteProctoringRoomDAO = remoteProctoringRoomDAO; } @Override @@ -130,31 +127,13 @@ public class ExamAdminServiceImpl implements ExamAdminService { return this.lmsAPIService .getLmsAPITemplate(exam.lmsSetupId) - .map(lmsAPI -> !lmsAPI.getSEBClientRestriction(exam).hasError()); + .map(lmsAPI -> lmsAPI.hasSEBClientRestriction(exam)) + .onError(error -> log.error("Failed to check SEB restriction: ", error)); } @Override public Result getProctoringServiceSettings(final Long examId) { - return this.additionalAttributesDAO.getAdditionalAttributes(EntityType.EXAM, examId) - .map(attrs -> attrs.stream() - .collect(Collectors.toMap( - attr -> attr.getName(), - Function.identity()))) - .map(mapping -> { - return new ProctoringServiceSettings( - examId, - getEnabled(mapping), - getServerType(mapping), - getString(mapping, ProctoringServiceSettings.ATTR_SERVER_URL), - getCollectingRoomSize(mapping), - getEnabledFeatures(mapping), - this.remoteProctoringRoomDAO.isServiceInUse(examId).getOr(true), - getString(mapping, ProctoringServiceSettings.ATTR_APP_KEY), - getString(mapping, ProctoringServiceSettings.ATTR_APP_SECRET), - getString(mapping, ProctoringServiceSettings.ATTR_SDK_KEY), - getString(mapping, ProctoringServiceSettings.ATTR_SDK_SECRET), - getBoolean(mapping, ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM)); - }); + return this.proctoringServiceSettingsService.getProctoringSettings(new EntityKey(examId, EntityType.EXAM)); } @Override @@ -163,79 +142,14 @@ public class ExamAdminServiceImpl implements ExamAdminService { final Long examId, final ProctoringServiceSettings proctoringServiceSettings) { - return Result.tryCatch(() -> { - - this.additionalAttributesDAO.saveAdditionalAttribute( - EntityType.EXAM, - examId, - ProctoringServiceSettings.ATTR_ENABLE_PROCTORING, - String.valueOf(proctoringServiceSettings.enableProctoring)); - - this.additionalAttributesDAO.saveAdditionalAttribute( - EntityType.EXAM, - examId, - ProctoringServiceSettings.ATTR_SERVER_TYPE, - proctoringServiceSettings.serverType.name()); - - this.additionalAttributesDAO.saveAdditionalAttribute( - EntityType.EXAM, - examId, - ProctoringServiceSettings.ATTR_SERVER_URL, - StringUtils.trim(proctoringServiceSettings.serverURL)); - - this.additionalAttributesDAO.saveAdditionalAttribute( - EntityType.EXAM, - examId, - ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE, - String.valueOf(proctoringServiceSettings.collectingRoomSize)); - - this.additionalAttributesDAO.saveAdditionalAttribute( - EntityType.EXAM, - examId, - ProctoringServiceSettings.ATTR_APP_KEY, - StringUtils.trim(proctoringServiceSettings.appKey)); - - this.additionalAttributesDAO.saveAdditionalAttribute( - EntityType.EXAM, - examId, - ProctoringServiceSettings.ATTR_APP_SECRET, - this.cryptor.encrypt(Utils.trim(proctoringServiceSettings.appSecret)) - .getOrThrow() - .toString()); - - if (StringUtils.isNotBlank(proctoringServiceSettings.sdkKey)) { - this.additionalAttributesDAO.saveAdditionalAttribute( - EntityType.EXAM, - examId, - ProctoringServiceSettings.ATTR_SDK_KEY, - StringUtils.trim(proctoringServiceSettings.sdkKey)); - - this.additionalAttributesDAO.saveAdditionalAttribute( - EntityType.EXAM, - examId, - ProctoringServiceSettings.ATTR_SDK_SECRET, - this.cryptor.encrypt(Utils.trim(proctoringServiceSettings.sdkSecret)) - .getOrThrow() - .toString()); - } - - this.additionalAttributesDAO.saveAdditionalAttribute( - EntityType.EXAM, - examId, - ProctoringServiceSettings.ATTR_ENABLED_FEATURES, - StringUtils.join(proctoringServiceSettings.enabledFeatures, Constants.LIST_SEPARATOR)); - - this.additionalAttributesDAO.saveAdditionalAttribute( - EntityType.EXAM, - examId, - ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM, - String.valueOf(proctoringServiceSettings.useZoomAppClientForCollectingRoom)); - - // Mark the involved exam as updated to notify changes - this.examDAO.setModified(examId); - - return proctoringServiceSettings; - }); + return this.proctoringServiceSettingsService + .saveProctoringServiceSettings( + new EntityKey(examId, EntityType.EXAM), + proctoringServiceSettings) + .map(settings -> { + this.examDAO.setModified(examId); + return settings; + }); } @Override @@ -256,78 +170,10 @@ public class ExamAdminServiceImpl implements ExamAdminService { } @Override - public Result getExamProctoringService(final ProctoringServerType type) { - return this.examProctoringServiceFactory - .getExamProctoringService(type); - } - - private Boolean getEnabled(final Map mapping) { - if (mapping.containsKey(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING)) { - return BooleanUtils.toBoolean(mapping.get(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING).getValue()); - } else { - return false; - } - } - - private ProctoringServerType getServerType(final Map mapping) { - if (mapping.containsKey(ProctoringServiceSettings.ATTR_SERVER_TYPE)) { - return ProctoringServerType.valueOf(mapping.get(ProctoringServiceSettings.ATTR_SERVER_TYPE).getValue()); - } else { - return ProctoringServerType.JITSI_MEET; - } - } - - private String getString(final Map mapping, final String name) { - if (mapping.containsKey(name)) { - return mapping.get(name).getValue(); - } else { - return null; - } - } - - private Boolean getBoolean(final Map mapping, final String name) { - if (mapping.containsKey(name)) { - return BooleanUtils.toBooleanObject(mapping.get(name).getValue()); - } else { - return false; - } - } - - private Integer getCollectingRoomSize(final Map mapping) { - if (mapping.containsKey(ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE)) { - return Integer.valueOf(mapping.get(ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE).getValue()); - } else { - return 20; - } - } - - private EnumSet getEnabledFeatures(final Map mapping) { - if (mapping.containsKey(ProctoringServiceSettings.ATTR_ENABLED_FEATURES)) { - try { - final String value = mapping.get(ProctoringServiceSettings.ATTR_ENABLED_FEATURES).getValue(); - return StringUtils.isNotBlank(value) - ? EnumSet.copyOf(Arrays.asList(StringUtils.split(value, Constants.LIST_SEPARATOR)) - .stream() - .map(str -> { - try { - return ProctoringFeature.valueOf(str); - } catch (final Exception e) { - log.error( - "Failed to enabled single features for proctoring settings. Skipping. {}", - e.getMessage()); - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toSet())) - : EnumSet.noneOf(ProctoringFeature.class); - } catch (final Exception e) { - log.error("Failed to get enabled features for proctoring settings. Enable all. {}", e.getMessage()); - return EnumSet.allOf(ProctoringFeature.class); - } - } else { - return EnumSet.allOf(ProctoringFeature.class); - } + public Result getExamProctoringService(final Long examId) { + return getProctoringServiceSettings(examId) + .flatMap(settings -> this.proctoringServiceSettingsService + .getExamProctoringService(settings.serverType)); } private Result saveAdditionalAttributesForMoodleExams(final Exam exam) { @@ -350,4 +196,55 @@ public class ExamAdminServiceImpl implements ExamAdminService { }); } + @Override + public Result archiveExam(final Exam exam) { + return Result.tryCatch(() -> { + + if (exam.status != ExamStatus.FINISHED) { + throw new APIMessageException( + APIMessage.ErrorMessage.INTEGRITY_VALIDATION.of("Exam is in wrong status to archive.")); + } + + if (log.isDebugEnabled()) { + log.debug("Archiving exam: {}", exam); + } + + if (this.isRestricted(exam).getOr(false)) { + + if (log.isDebugEnabled()) { + log.debug("Archiving exam, SEB restriction still active, try to release: {}", exam); + } + + this.lmsAPIService + .getLmsAPITemplate(exam.lmsSetupId) + .flatMap(lms -> lms.releaseSEBClientRestriction(exam)) + .onError(error -> log.error( + "Failed to release SEB client restriction for archiving exam: ", + error)); + } + + final Exam result = this.examDAO + .updateState(exam.id, ExamStatus.ARCHIVED, null) + .getOrThrow(); + + this.examConfigurationMapDAO + .getConfigurationNodeIds(result.id) + .getOrThrow() + .stream() + .forEach(configNodeId -> { + if (this.examConfigurationMapDAO.checkNoActiveExamReferences(configNodeId).getOr(false)) { + log.debug("Also set exam configuration to archived: ", configNodeId); + this.configurationNodeDAO.save( + new ConfigurationNode( + configNodeId, null, null, null, null, null, + null, ConfigurationStatus.ARCHIVED, null, null)) + .onError(error -> log.error("Failed to set exam configuration to archived state: ", + error)); + } + }); + + return result; + }); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamTemplateServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamTemplateServiceImpl.java index 973d3814..cd0f88a0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamTemplateServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamTemplateServiceImpl.java @@ -28,6 +28,7 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; +import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate; @@ -44,6 +45,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamTemplateDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.IndicatorDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamTemplateService; @@ -118,13 +120,14 @@ public class ExamTemplateServiceImpl implements ExamTemplateService { @Override public Result initAdditionalAttributes(final Exam exam) { - return this.examAdminService.saveLMSAttributes(exam) + return this.examAdminService + .saveLMSAttributes(exam) .map(_exam -> { if (exam.examTemplateId != null) { if (log.isDebugEnabled()) { - log.debug("Init exam: {} with additional attributes form exam template: {}", + log.debug("Init exam: {} with additional attributes from exam template: {}", exam.externalId, exam.examTemplateId); } @@ -148,7 +151,8 @@ public class ExamTemplateServiceImpl implements ExamTemplateService { } } return _exam; - }).onError(error -> log.error("Failed to create additional attributes defined by template for exam: ", + }).onError(error -> log.error( + "Failed to create additional attributes defined by template for exam: ", error)); } @@ -175,28 +179,7 @@ public class ExamTemplateServiceImpl implements ExamTemplateService { if (examTemplate.configTemplateId != null) { - // create new exam configuration for the exam - final ConfigurationNode configurationNode = new ConfigurationNode( - null, - exam.institutionId, - examTemplate.configTemplateId, - replaceVars(this.defaultExamConfigNameTemplate, exam, examTemplate), - replaceVars(this.defaultExamConfigDescTemplate, exam, examTemplate), - ConfigurationType.EXAM_CONFIG, - exam.owner, - ConfigurationStatus.IN_USE); - - final ConfigurationNode examConfig = this.configurationNodeDAO - .createNew(configurationNode) - .onError(error -> log.error( - "Failed to create exam configuration for exam: {} from template: {} examConfig: {}", - exam.name, - examTemplate.name, - configurationNode, - error)) - .getOrThrow(error -> new APIMessageException( - ErrorMessage.EXAM_IMPORT_ERROR_AUTO_CONFIG, - error)); + final ConfigurationNode examConfig = createOrReuseConfig(exam, examTemplate); // map the exam configuration to the exam this.examConfigurationMapDAO.createNew(new ExamConfigurationMap( @@ -224,6 +207,72 @@ public class ExamTemplateServiceImpl implements ExamTemplateService { }).onError(error -> log.error("Failed to create exam configuration defined by template for exam: ", error)); } + private ConfigurationNode createOrReuseConfig(final Exam exam, final ExamTemplate examTemplate) { + final String configName = replaceVars(this.defaultExamConfigNameTemplate, exam, examTemplate); + final FilterMap filterMap = new FilterMap(); + filterMap.putIfAbsent(Entity.FILTER_ATTR_INSTITUTION, exam.institutionId.toString()); + filterMap.putIfAbsent(Entity.FILTER_ATTR_NAME, configName); + + // get existing config if available + final ConfigurationNode examConfig = this.configurationNodeDAO + .allMatching(filterMap) + .getOrThrow() + .stream() + .filter(res -> res.name.equals(configName)) + .findFirst() + .orElse(null); + + if (examConfig == null || examConfig.status != ConfigurationStatus.READY_TO_USE) { + final ConfigurationNode config = new ConfigurationNode( + null, + exam.institutionId, + examTemplate.configTemplateId, + configName, + replaceVars(this.defaultExamConfigDescTemplate, exam, examTemplate), + ConfigurationType.EXAM_CONFIG, + exam.owner, + ConfigurationStatus.IN_USE, + null, + null); + + return this.configurationNodeDAO + .createNew(config) + .onError(error -> log.error( + "Failed to create exam configuration for exam: {} from template: {} examConfig: {}", + exam.name, + examTemplate.name, + config, + error)) + .getOrThrow(error -> new APIMessageException( + ErrorMessage.EXAM_IMPORT_ERROR_AUTO_CONFIG, + error)); + } else { + final ConfigurationNode config = new ConfigurationNode( + examConfig.id, + null, + null, + null, + null, + null, + null, + ConfigurationStatus.IN_USE, + null, + null); + + return this.configurationNodeDAO + .save(config) + .onError(error -> log.error( + "Failed to save exam configuration for exam: {} from template: {} examConfig: {}", + exam.name, + examTemplate.name, + config, + error)) + .getOrThrow(error -> new APIMessageException( + ErrorMessage.EXAM_IMPORT_ERROR_AUTO_CONFIG, + error)); + } + } + private Result addIndicatorsFromTemplate(final Exam exam) { return Result.tryCatch(() -> { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ProctoringAdminServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ProctoringAdminServiceImpl.java new file mode 100644 index 00000000..897ead0b --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ProctoringAdminServiceImpl.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.exam.impl; + +import java.util.Arrays; +import java.util.EnumSet; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; +import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature; +import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType; +import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Cryptor; +import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.ExamProctoringServiceFactory; + +@Lazy +@Service +@WebServiceProfile +public class ProctoringAdminServiceImpl implements ProctoringAdminService { + + private static final Logger log = LoggerFactory.getLogger(ProctoringAdminServiceImpl.class); + + private final AdditionalAttributesDAO additionalAttributesDAO; + private final RemoteProctoringRoomDAO remoteProctoringRoomDAO; + private final ExamProctoringServiceFactory examProctoringServiceFactory; + private final Cryptor cryptor; + + public ProctoringAdminServiceImpl( + final AdditionalAttributesDAO additionalAttributesDAO, + final RemoteProctoringRoomDAO remoteProctoringRoomDAO, + final ExamProctoringServiceFactory examProctoringServiceFactory, + final Cryptor cryptor) { + + this.additionalAttributesDAO = additionalAttributesDAO; + this.remoteProctoringRoomDAO = remoteProctoringRoomDAO; + this.examProctoringServiceFactory = examProctoringServiceFactory; + this.cryptor = cryptor; + } + + @Override + @Transactional(readOnly = true) + public Result getProctoringSettings(final EntityKey parentEntityKey) { + + return Result.tryCatch(() -> { + final Long entityId = Long.parseLong(parentEntityKey.modelId); + checkType(parentEntityKey); + + return this.additionalAttributesDAO + .getAdditionalAttributes(parentEntityKey.entityType, entityId) + .map(attrs -> attrs.stream() + .collect(Collectors.toMap( + attr -> attr.getName(), + Function.identity()))) + .map(mapping -> { + return new ProctoringServiceSettings( + entityId, + getEnabled(mapping), + getServerType(mapping), + getString(mapping, ProctoringServiceSettings.ATTR_SERVER_URL), + getCollectingRoomSize(mapping), + getEnabledFeatures(mapping), + parentEntityKey.entityType == EntityType.EXAM + ? this.remoteProctoringRoomDAO.isServiceInUse(entityId).getOr(true) + : false, + getString(mapping, ProctoringServiceSettings.ATTR_APP_KEY), + getString(mapping, ProctoringServiceSettings.ATTR_APP_SECRET), + getString(mapping, ProctoringServiceSettings.ATTR_SDK_KEY), + getString(mapping, ProctoringServiceSettings.ATTR_SDK_SECRET), + getBoolean(mapping, + ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM)); + }) + .getOrThrow(); + + }); + } + + @Override + @Transactional + public Result saveProctoringServiceSettings( + final EntityKey parentEntityKey, + final ProctoringServiceSettings proctoringServiceSettings) { + + return Result.tryCatch(() -> { + final Long entityId = Long.parseLong(parentEntityKey.modelId); + checkType(parentEntityKey); + + if (StringUtils.isNotBlank(proctoringServiceSettings.serverURL)) { + testExamProctoring(proctoringServiceSettings).getOrThrow(); + } + + this.additionalAttributesDAO.saveAdditionalAttribute( + parentEntityKey.entityType, + entityId, + ProctoringServiceSettings.ATTR_ENABLE_PROCTORING, + String.valueOf(proctoringServiceSettings.enableProctoring)); + + this.additionalAttributesDAO.saveAdditionalAttribute( + parentEntityKey.entityType, + entityId, + ProctoringServiceSettings.ATTR_SERVER_TYPE, + proctoringServiceSettings.serverType.name()); + + this.additionalAttributesDAO.saveAdditionalAttribute( + parentEntityKey.entityType, + entityId, + ProctoringServiceSettings.ATTR_SERVER_URL, + StringUtils.trim(proctoringServiceSettings.serverURL)); + + this.additionalAttributesDAO.saveAdditionalAttribute( + parentEntityKey.entityType, + entityId, + ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE, + String.valueOf(proctoringServiceSettings.collectingRoomSize)); + + this.additionalAttributesDAO.saveAdditionalAttribute( + parentEntityKey.entityType, + entityId, + ProctoringServiceSettings.ATTR_APP_KEY, + StringUtils.trim(proctoringServiceSettings.appKey)); + + this.additionalAttributesDAO.saveAdditionalAttribute( + parentEntityKey.entityType, + entityId, + ProctoringServiceSettings.ATTR_APP_SECRET, + this.cryptor.encrypt(Utils.trim(proctoringServiceSettings.appSecret)) + .getOrThrow() + .toString()); + + if (StringUtils.isNotBlank(proctoringServiceSettings.sdkKey)) { + this.additionalAttributesDAO.saveAdditionalAttribute( + parentEntityKey.entityType, + entityId, + ProctoringServiceSettings.ATTR_SDK_KEY, + StringUtils.trim(proctoringServiceSettings.sdkKey)); + + this.additionalAttributesDAO.saveAdditionalAttribute( + parentEntityKey.entityType, + entityId, + ProctoringServiceSettings.ATTR_SDK_SECRET, + this.cryptor.encrypt(Utils.trim(proctoringServiceSettings.sdkSecret)) + .getOrThrow() + .toString()); + } + + this.additionalAttributesDAO.saveAdditionalAttribute( + parentEntityKey.entityType, + entityId, + ProctoringServiceSettings.ATTR_ENABLED_FEATURES, + StringUtils.join(proctoringServiceSettings.enabledFeatures, Constants.LIST_SEPARATOR)); + + this.additionalAttributesDAO.saveAdditionalAttribute( + parentEntityKey.entityType, + entityId, + ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM, + String.valueOf(proctoringServiceSettings.useZoomAppClientForCollectingRoom)); + + return proctoringServiceSettings; + }); + } + + @Override + public Result getExamProctoringService(final ProctoringServerType type) { + return this.examProctoringServiceFactory + .getExamProctoringService(type); + } + + private void checkType(final EntityKey parentEntityKey) { + if (!SUPPORTED_PARENT_ENTITES.contains(parentEntityKey.entityType)) { + throw new UnsupportedOperationException( + "No proctoring service settings supported for entity: " + parentEntityKey); + } + } + + private Boolean getEnabled(final Map mapping) { + if (mapping.containsKey(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING)) { + return BooleanUtils.toBoolean(mapping.get(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING).getValue()); + } else { + return false; + } + } + + private ProctoringServerType getServerType(final Map mapping) { + if (mapping.containsKey(ProctoringServiceSettings.ATTR_SERVER_TYPE)) { + return ProctoringServerType.valueOf(mapping.get(ProctoringServiceSettings.ATTR_SERVER_TYPE).getValue()); + } else { + return ProctoringServerType.JITSI_MEET; + } + } + + private String getString(final Map mapping, final String name) { + if (mapping.containsKey(name)) { + return mapping.get(name).getValue(); + } else { + return null; + } + } + + private Boolean getBoolean(final Map mapping, final String name) { + if (mapping.containsKey(name)) { + return BooleanUtils.toBooleanObject(mapping.get(name).getValue()); + } else { + return false; + } + } + + private Integer getCollectingRoomSize(final Map mapping) { + if (mapping.containsKey(ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE)) { + return Integer.valueOf(mapping.get(ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE).getValue()); + } else { + return 20; + } + } + + private EnumSet getEnabledFeatures(final Map mapping) { + if (mapping.containsKey(ProctoringServiceSettings.ATTR_ENABLED_FEATURES)) { + try { + final String value = mapping.get(ProctoringServiceSettings.ATTR_ENABLED_FEATURES).getValue(); + return StringUtils.isNotBlank(value) + ? EnumSet.copyOf(Arrays.asList(StringUtils.split(value, Constants.LIST_SEPARATOR)) + .stream() + .map(str -> { + try { + return ProctoringFeature.valueOf(str); + } catch (final Exception e) { + log.error( + "Failed to enabled single features for proctoring settings. Skipping. {}", + e.getMessage()); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toSet())) + : EnumSet.noneOf(ProctoringFeature.class); + } catch (final Exception e) { + log.error("Failed to get enabled features for proctoring settings. Enable all. {}", e.getMessage()); + return EnumSet.allOf(ProctoringFeature.class); + } + } else { + return EnumSet.allOf(ProctoringFeature.class); + } + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/CourseAccessAPI.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/CourseAccessAPI.java new file mode 100644 index 00000000..026c23e5 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/CourseAccessAPI.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.lms; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; +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.dao.FilterMap; + +public interface CourseAccessAPI { + + Logger log = LoggerFactory.getLogger(CourseAccessAPI.class); + + /** Fetch status that indicates an asynchronous quiz data fetch status if the + * concrete implementation has such. */ + public enum FetchStatus { + ALL_FETCHED, + ASYNC_FETCH_RUNNING, + FETCH_ERROR + } + + /** Performs a test for the underling {@link LmsSetup } configuration and checks if the + * LMS and the course API of the LMS can be accessed or if there are some difficulties, + * missing configuration data or connection/authentication errors. + * + * @return {@link LmsSetupTestResult } instance with the test result report */ + LmsSetupTestResult testCourseAccessAPI(); + + /** To make a quick course API access check without report that just throws an exception if not available */ + default void checkCourseAPIAccess() { + final LmsSetupTestResult testCourseAccessAPI = this.testCourseAccessAPI(); + if (!testCourseAccessAPI.isOk()) { + throw new RuntimeException("No course API Access: " + testCourseAccessAPI); + } + } + + /** Get an unsorted List of filtered {@link QuizData } from the LMS course/quiz API + * + * @param filterMap the {@link FilterMap } to get a filtered result. Possible filter attributes are: + * + *
+     *      {@link QuizData.FILTER_ATTR_QUIZ_NAME } The quiz name filter text (exclude all names that do not contain the given text)
+     *      {@link QuizData.FILTER_ATTR_START_TIME } The quiz start time (exclude all quizzes that starts before)
+     *            
+ * + * @return Result of an unsorted List of filtered {@link QuizData } from the LMS course/quiz API + * or refer to an error when happened */ + Result> getQuizzes(FilterMap filterMap); + + /** Get all {@link QuizData } for the set of {@link QuizData } identifiers from LMS API in a collection + * of Result. If particular quizzes cannot be loaded because of errors or deletion, + * the the referencing QuizData will not be in the resulting list and an error is logged. + * + * @param ids the Set of Quiz identifiers to get the {@link QuizData } for + * @return Collection of all {@link QuizData } from the given id set */ + Result> getQuizzes(Set ids); + + /** Get the quiz data with specified identifier. + * + * @param id the quiz data identifier + * @return Result refer to the quiz data or to an error when happened */ + Result getQuiz(final String id); + + /** Clears the underling caches if there are some for a particular implementation. */ + void clearCourseCache(); + + /** Convert an anonymous or temporary examineeUserId, sent by the SEB Client on LMS login, + * to LMS examinee account details by requesting them on the LMS API with the given examineeUserId + * + * @param examineeUserId the examinee user identifier derived from SEB Client + * @return a Result refer to the {@link ExamineeAccountDetails } instance or to an error when happened or not + * supported */ + Result getExamineeAccountDetails(String examineeUserId); + + /** Used to convert an anonymous or temporary examineeUserId, sent by the SEB Client on LMS login, + * 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, + * the given examineeSessionId shall be returned. + * + * @param examineeUserId the examinee user identifier derived from SEB Client + * @return a user account display name if supported or the given examineeSessionId if not. */ + String getExamineeName(final String examineeUserId); + + /** Used to get a list of chapters (display name and chapter-identifier) that can be used to + * apply chapter-based SEB restriction for a specified course. + * + * The availability of this depends on the type of LMS and on installed plugins that supports this feature. + * If this is not supported by the underling LMS a UnsupportedOperationException will be presented + * within the Result. + * + * @param courseId The course identifier + * @return Result referencing to the Chapters model for the given course or to an error when happened. */ + Result getCourseChapters(String courseId); + + default FetchStatus getFetchStatus() { + return FetchStatus.ALL_FETCHED; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/LmsAPIService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/LmsAPIService.java index 8ca736fd..285a185e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/LmsAPIService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/LmsAPIService.java @@ -60,7 +60,7 @@ public interface LmsAPIService { * @param pageSize the page size * @param sort the sort parameter * @param filterMap the FilterMap containing all filter criteria - * @return the specified Page of QuizData form all active LMS Setups of the current users institution */ + * @return the specified Page of QuizData from all active LMS Setups of the current users institution */ Result> requestQuizDataPage( final int pageNumber, final int pageSize, @@ -139,7 +139,7 @@ public interface LmsAPIService { * @param sortAttribute the sort attribute for the new Page * @param pageNumber the number of the Page to build * @param pageSize the size of the Page to build - * @return A Page of QuizData extracted form a given list of QuizData */ + * @return A Page of QuizData extracted from a given list of QuizData */ static Function, Page> quizzesToPageFunction( final String sortAttribute, final int pageNumber, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/LmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/LmsAPITemplate.java index 8b990a67..d739dd40 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/LmsAPITemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/LmsAPITemplate.java @@ -8,22 +8,10 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms; -import java.util.Collection; -import java.util.List; -import java.util.Set; - import ch.ethz.seb.sebserver.gbl.async.CircuitBreaker; -import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; -import ch.ethz.seb.sebserver.gbl.model.exam.Exam; -import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; -import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; 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.dao.FilterMap; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCachedCourseAccess; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCourseAccess; /** Defines an LMS API access template to build SEB Server LMS integration. *

@@ -75,7 +63,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCourseAcce * or partial API Access and can flag missing or wrong {@link LmsSetup } attributes with the resulting * {@link LmsSetupTestResult }.
* SEB Server than uses an instance of this template to communicate with the an LMS. */ -public interface LmsAPITemplate { +public interface LmsAPITemplate extends CourseAccessAPI, SEBRestrictionAPI { /** Get the LMS type of the concrete template implementation * @@ -87,110 +75,8 @@ public interface LmsAPITemplate { * @return the underling {@link LmsSetup } configuration for this LmsAPITemplate */ LmsSetup lmsSetup(); - // ******************************************************************* - // **** Course API functions ***************************************** - - /** Performs a test for the underling {@link LmsSetup } configuration and checks if the - * LMS and the course API of the LMS can be accessed or if there are some difficulties, - * missing configuration data or connection/authentication errors. - * - * @return {@link LmsSetupTestResult } instance with the test result report */ - LmsSetupTestResult testCourseAccessAPI(); - - /** Get an unsorted List of filtered {@link QuizData } from the LMS course/quiz API - * - * @param filterMap the {@link FilterMap } to get a filtered result. Possible filter attributes are: - * - *
-     *      {@link QuizData.FILTER_ATTR_QUIZ_NAME } The quiz name filter text (exclude all names that do not contain the given text)
-     *      {@link QuizData.FILTER_ATTR_START_TIME } The quiz start time (exclude all quizzes that starts before)
-     *            
- * - * @return Result of an unsorted List of filtered {@link QuizData } from the LMS course/quiz API - * or refer to an error when happened */ - Result> getQuizzes(FilterMap filterMap); - - /** Get all {@link QuizData } for the set of {@link QuizData } identifiers from LMS API in a collection - * of Result. If particular quizzes cannot be loaded because of errors or deletion, - * the the referencing QuizData will not be in the resulting list and an error is logged. - * - * @param ids the Set of Quiz identifiers to get the {@link QuizData } for - * @return Collection of all {@link QuizData } from the given id set */ - Result> getQuizzes(Set ids); - - /** Get the quiz data with specified identifier. - * - * @param id the quiz data identifier - * @return Result refer to the quiz data or to an error when happened */ - Result getQuiz(final String id); - - /** Clears the underling caches if there are some for a particular implementation. */ - void clearCache(); - - /** Convert an anonymous or temporary examineeUserId, sent by the SEB Client on LMS login, - * to LMS examinee account details by requesting them on the LMS API with the given examineeUserId - * - * @param examineeUserId the examinee user identifier derived from SEB Client - * @return a Result refer to the {@link ExamineeAccountDetails } instance or to an error when happened or not - * supported */ - Result getExamineeAccountDetails(String examineeUserId); - - /** Used to convert an anonymous or temporary examineeUserId, sent by the SEB Client on LMS login, - * 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, - * the given examineeSessionId shall be returned. - * - * @param examineeUserId the examinee user identifier derived from SEB Client - * @return a user account display name if supported or the given examineeSessionId if not. */ - String getExamineeName(String examineeUserId); - - /** Used to get a list of chapters (display name and chapter-identifier) that can be used to - * apply chapter-based SEB restriction for a specified course. - * - * The availability of this depends on the type of LMS and on installed plugins that supports this feature. - * If this is not supported by the underling LMS a UnsupportedOperationException will be presented - * within the Result. - * - * @param courseId The course identifier - * @return Result referencing to the Chapters model for the given course or to an error when happened. */ - Result getCourseChapters(String courseId); - - // **************************************************************************** - // **** SEB restriction API functions ***************************************** - - /** Performs a test for the underling {@link LmsSetup } configuration and checks if the - * LMS and the course restriction API of the LMS can be accessed or if there are some difficulties, - * missing configuration data or connection/authentication errors. - * - * @return {@link LmsSetupTestResult } instance with the test result report */ - LmsSetupTestResult testCourseRestrictionAPI(); - - /** Get SEB restriction data form LMS within a {@link SEBRestrictionData } instance. The available restriction - * details - * 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 - * @return Result refer to the {@link SEBRestrictionData } instance or to an ResourceNotFoundException if the - * restriction is - * missing or to another exception on unexpected error case */ - Result getSEBClientRestriction(Exam exam); - - /** Applies SEB Client restrictions to the LMS with the given attributes. - * - * @param externalExamId The exam/course identifier from LMS side (Exam.externalId) - * @param sebRestrictionData containing all data for SEB Client restriction to apply to the LMS - * @return Result refer to the given {@link SEBRestrictionData } if restriction was successful or to an error if - * not */ - Result applySEBClientRestriction( - String externalExamId, - SEBRestriction sebRestrictionData); - - /** Releases an already applied SEB Client restriction within the LMS for a given Exam. - * This completely removes the SEB Client restriction on LMS side. - * - * @param exam the Exam to release the restriction for. - * @return Result refer to the given Exam if successful or to an error if not */ - Result releaseSEBClientRestriction(Exam exam); + default void dispose() { + clearCourseCache(); + } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/SEBRestrictionAPI.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/SEBRestrictionAPI.java new file mode 100644 index 00000000..a88786ee --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/SEBRestrictionAPI.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.lms; + +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult; +import ch.ethz.seb.sebserver.gbl.util.Result; + +public interface SEBRestrictionAPI { + + /** Performs a test for the underling {@link LmsSetup } configuration and checks if the + * LMS and the course restriction API of the LMS can be accessed or if there are some difficulties, + * missing configuration data or connection/authentication errors. + * + * @return {@link LmsSetupTestResult } instance with the test result report */ + LmsSetupTestResult testCourseRestrictionAPI(); + + /** Get SEB restriction data from LMS within a {@link SEBRestrictionData } instance. The available restriction + * details + * 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 + * @return Result refer to the {@link SEBRestrictionData } instance or to an ResourceNotFoundException if the + * restriction is + * missing or to another exception on unexpected error case */ + Result getSEBClientRestriction(Exam exam); + + /** Use this to check if there is a SEB restriction available on the LMS for the specified exam. + * + * A SEB Restriction is available if there it can get from LMS and if there is either a Config-Key + * or a BrowserExam-Key set or both. If none of this keys is set, the SEB Restriction is been + * considered to not set on the LMS. + * + * @param exam exam the exam to get the SEB restriction data for + * @return true if there is a SEB restriction set on the LMS for the exam or false otherwise */ + default boolean hasSEBClientRestriction(final Exam exam) { + final Result sebClientRestriction = getSEBClientRestriction(exam); + if (sebClientRestriction.hasError()) { + return false; + } + + final SEBRestriction sebRestriction = sebClientRestriction.get(); + return !sebRestriction.configKeys.isEmpty() || !sebRestriction.browserExamKeys.isEmpty(); + } + + /** Applies SEB Client restrictions to the LMS with the given attributes. + * + * @param externalExamId The exam/course identifier from LMS side (Exam.externalId) + * @param sebRestrictionData containing all data for SEB Client restriction to apply to the LMS + * @return Result refer to the given {@link SEBRestrictionData } if restriction was successful or to an error if + * not */ + Result applySEBClientRestriction( + String externalExamId, + SEBRestriction sebRestrictionData); + + /** Releases an already applied SEB Client restriction within the LMS for a given Exam. + * This completely removes the SEB Client restriction on LMS side. + * + * @param exam the Exam to release the restriction for. + * @return Result refer to the given Exam if successful or to an error if not */ + Result releaseSEBClientRestriction(Exam exam); + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/AbstractCachedCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/AbstractCachedCourseAccess.java index 7e2a186a..1e78e578 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/AbstractCachedCourseAccess.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/AbstractCachedCourseAccess.java @@ -16,10 +16,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; -import org.springframework.core.env.Environment; import ch.ethz.seb.sebserver.gbl.Constants; -import ch.ethz.seb.sebserver.gbl.async.AsyncService; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; /** This implements an overall short time cache for QuizData objects for all implementing @@ -28,7 +26,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; * The QuizData are stored with a key composed from the id of the key *

* The EH-Cache can be configured in file ehcache.xml **/ -public abstract class AbstractCachedCourseAccess extends AbstractCourseAccess { +public abstract class AbstractCachedCourseAccess { private static final Logger log = LoggerFactory.getLogger(AbstractCachedCourseAccess.class); @@ -37,17 +35,12 @@ public abstract class AbstractCachedCourseAccess extends AbstractCourseAccess { private final Cache cache; - protected AbstractCachedCourseAccess( - final AsyncService asyncService, - final Environment environment, - final CacheManager cacheManager) { - - super(asyncService, environment); + protected AbstractCachedCourseAccess(final CacheManager cacheManager) { this.cache = cacheManager.getCache(CACHE_NAME_QUIZ_DATA); } /** Used to clear the entire cache */ - public void clearCache() { + public void clearCourseCache() { final Object nativeCache = this.cache.getNativeCache(); if (nativeCache instanceof javax.cache.Cache) { try { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/AbstractCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/AbstractCourseAccess.java deleted file mode 100644 index 5a5f3a3c..00000000 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/AbstractCourseAccess.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET) - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl; - -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.function.Supplier; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.env.Environment; - -import ch.ethz.seb.sebserver.gbl.Constants; -import ch.ethz.seb.sebserver.gbl.async.AsyncService; -import ch.ethz.seb.sebserver.gbl.async.CircuitBreaker; -import ch.ethz.seb.sebserver.gbl.async.CircuitBreaker.State; -import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; -import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; -import ch.ethz.seb.sebserver.gbl.model.user.ExamineeAccountDetails; -import ch.ethz.seb.sebserver.gbl.util.Result; -import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; - -/** A partial course access API implementation that uses CircuitBreaker to apply LMS - * API requests in a protected environment. - * - * Extend this to implement a concrete course access API for a given type of LMS. */ -public abstract class AbstractCourseAccess { - - private static final Logger log = LoggerFactory.getLogger(AbstractCourseAccess.class); - - /** Fetch status that indicates an asynchronous quiz data fetch status if the - * concrete implementation has such. */ - public enum FetchStatus { - ALL_FETCHED, - ASYNC_FETCH_RUNNING, - FETCH_ERROR - } - - /** CircuitBreaker for protected quiz and course data requests */ - protected final CircuitBreaker> allQuizzesRequest; - /** CircuitBreaker for protected quiz and course data requests */ - protected final CircuitBreaker> quizzesRequest; - /** CircuitBreaker for protected quiz and course data requests */ - protected final CircuitBreaker quizRequest; - /** CircuitBreaker for protected chapter data requests */ - protected final CircuitBreaker chaptersRequest; - /** CircuitBreaker for protected examinee account details requests */ - protected final CircuitBreaker accountDetailRequest; - - protected AbstractCourseAccess( - final AsyncService asyncService, - final Environment environment) { - - this.allQuizzesRequest = asyncService.createCircuitBreaker( - environment.getProperty( - "sebserver.webservice.circuitbreaker.quizzesRequest.attempts", - Integer.class, - 3), - environment.getProperty( - "sebserver.webservice.circuitbreaker.quizzesRequest.blockingTime", - Long.class, - Constants.MINUTE_IN_MILLIS), - environment.getProperty( - "sebserver.webservice.circuitbreaker.quizzesRequest.timeToRecover", - Long.class, - Constants.MINUTE_IN_MILLIS)); - - this.quizzesRequest = asyncService.createCircuitBreaker( - environment.getProperty( - "sebserver.webservice.circuitbreaker.quizzesRequest.attempts", - Integer.class, - 3), - environment.getProperty( - "sebserver.webservice.circuitbreaker.quizzesRequest.blockingTime", - Long.class, - Constants.SECOND_IN_MILLIS * 10), - environment.getProperty( - "sebserver.webservice.circuitbreaker.quizzesRequest.timeToRecover", - Long.class, - Constants.MINUTE_IN_MILLIS)); - - this.quizRequest = asyncService.createCircuitBreaker( - environment.getProperty( - "sebserver.webservice.circuitbreaker.quizzesRequest.attempts", - Integer.class, - 3), - environment.getProperty( - "sebserver.webservice.circuitbreaker.quizzesRequest.blockingTime", - Long.class, - Constants.SECOND_IN_MILLIS * 10), - environment.getProperty( - "sebserver.webservice.circuitbreaker.quizzesRequest.timeToRecover", - Long.class, - Constants.MINUTE_IN_MILLIS)); - - this.chaptersRequest = asyncService.createCircuitBreaker( - environment.getProperty( - "sebserver.webservice.circuitbreaker.chaptersRequest.attempts", - Integer.class, - 3), - environment.getProperty( - "sebserver.webservice.circuitbreaker.chaptersRequest.blockingTime", - Long.class, - Constants.SECOND_IN_MILLIS * 10), - environment.getProperty( - "sebserver.webservice.circuitbreaker.chaptersRequest.timeToRecover", - Long.class, - Constants.SECOND_IN_MILLIS * 30)); - - this.accountDetailRequest = asyncService.createCircuitBreaker( - environment.getProperty( - "sebserver.webservice.circuitbreaker.accountDetailRequest.attempts", - Integer.class, - 2), - environment.getProperty( - "sebserver.webservice.circuitbreaker.accountDetailRequest.blockingTime", - Long.class, - Constants.SECOND_IN_MILLIS * 10), - environment.getProperty( - "sebserver.webservice.circuitbreaker.accountDetailRequest.timeToRecover", - Long.class, - Constants.SECOND_IN_MILLIS * 30)); - } - - public Result> protectedQuizzesRequest(final FilterMap filterMap) { - return this.allQuizzesRequest.protectedRun(allQuizzesSupplier(filterMap)) - .onError(error -> log.error( - "Failed to run protectedQuizzesRequest: {}", - error.getMessage())); - } - - public Result> protectedQuizzesRequest(final Set ids) { - return this.quizzesRequest.protectedRun(quizzesSupplier(ids)) - .onError(error -> log.error( - "Failed to run protectedQuizzesRequest: {}", - error.getMessage())); - } - - public Result protectedQuizRequest(final String id) { - return this.quizRequest.protectedRun(quizSupplier(id)) - .onError(error -> log.error( - "Failed to run protectedQuizRequest: {}", - error.getMessage())); - } - - public Result getExamineeAccountDetails(final String examineeSessionId) { - final Supplier accountDetailsSupplier = accountDetailsSupplier(examineeSessionId); - return this.accountDetailRequest.protectedRun(() -> { - try { - return accountDetailsSupplier.get(); - } catch (final Exception e) { - log.error("Unexpected error while trying to get examinee account details: ", e); - throw e; - } - }); - } - - /** Default implementation that uses getExamineeAccountDetails to geht the examinee name - * - * @param examineeSessionId - * @return The examinee account name for the given examineeSessionId */ - public String getExamineeName(final String examineeSessionId) { - return getExamineeAccountDetails(examineeSessionId) - .map(ExamineeAccountDetails::getDisplayName) - .onError(error -> log.warn("Failed to request user-name for ID: {}", error.getMessage(), error)) - .getOr(examineeSessionId); - } - - public Result getCourseChapters(final String courseId) { - return this.chaptersRequest.protectedRun(getCourseChaptersSupplier(courseId)) - .onError(error -> log.error( - "Failed to run getCourseChapters: {}", - error.getMessage())); - } - - protected abstract Supplier accountDetailsSupplier(final String examineeSessionId); - - /** Provides a supplier to supply request to use within the circuit breaker */ - protected abstract Supplier> allQuizzesSupplier(final FilterMap filterMap); - - /** Provides a supplier for the quiz data request to use within the circuit breaker */ - protected abstract Supplier> quizzesSupplier(final Set ids); - - /** Provides a supplier for the quiz data request to use within the circuit breaker */ - protected abstract Supplier quizSupplier(final String id); - - /** Provides a supplier for the course chapter data request to use within the circuit breaker */ - protected abstract Supplier getCourseChaptersSupplier(final String courseId); - - protected FetchStatus getFetchStatus() { - if (this.quizzesRequest.getState() != State.CLOSED) { - return FetchStatus.FETCH_ERROR; - } - return FetchStatus.ALL_FETCHED; - } -} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java index f873767b..8fa65052 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java @@ -93,15 +93,17 @@ public class LmsAPIServiceImpl implements LmsAPIService { log.debug("LmsSetup changed. Update cache by removing eventually used references"); } - final LmsAPITemplate removedTemplate = this.cache - .remove(new CacheKey(lmsSetup.getModelId(), 0)); + final LmsAPITemplate removedTemplate = this.cache.remove( + new CacheKey(lmsSetup.getModelId(), 0)); + if (removedTemplate != null) { - removedTemplate.clearCache(); + removedTemplate.clearCourseCache(); } } @Override public void cleanup() { + this.cache.values().forEach(LmsAPITemplate::dispose); this.cache.clear(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPITemplateAdapter.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPITemplateAdapter.java new file mode 100644 index 00000000..71f79253 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPITemplateAdapter.java @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.async.AsyncService; +import ch.ethz.seb.sebserver.gbl.async.CircuitBreaker; +import ch.ethz.seb.sebserver.gbl.async.CircuitBreaker.State; +import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; +import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; +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.dao.FilterMap; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.CourseAccessAPI; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionAPI; + +public class LmsAPITemplateAdapter implements LmsAPITemplate { + + private static final Logger log = LoggerFactory.getLogger(LmsAPITemplateAdapter.class); + + private final CourseAccessAPI courseAccessAPI; + private final SEBRestrictionAPI sebRestrictionAPI; + private final APITemplateDataSupplier apiTemplateDataSupplier; + + /** CircuitBreaker for protected lmsTestRequest */ + private final CircuitBreaker lmsTestRequest; + /** CircuitBreaker for protected quiz and course data requests */ + private final CircuitBreaker> allQuizzesRequest; + /** CircuitBreaker for protected quiz and course data requests */ + private final CircuitBreaker> quizzesRequest; + /** CircuitBreaker for protected quiz and course data requests */ + private final CircuitBreaker quizRequest; + /** CircuitBreaker for protected chapter data requests */ + private final CircuitBreaker chaptersRequest; + /** CircuitBreaker for protected examinee account details requests */ + private final CircuitBreaker accountDetailRequest; + + private final CircuitBreaker restrictionRequest; + private final CircuitBreaker releaseRestrictionRequest; + + public LmsAPITemplateAdapter( + final AsyncService asyncService, + final Environment environment, + final APITemplateDataSupplier apiTemplateDataSupplier, + final CourseAccessAPI courseAccessAPI, + final SEBRestrictionAPI sebRestrictionAPI) { + + this.courseAccessAPI = courseAccessAPI; + this.sebRestrictionAPI = sebRestrictionAPI; + this.apiTemplateDataSupplier = apiTemplateDataSupplier; + + this.lmsTestRequest = asyncService.createCircuitBreaker( + environment.getProperty( + "sebserver.webservice.circuitbreaker.lmsTestRequest.attempts", + Integer.class, + 2), + environment.getProperty( + "sebserver.webservice.circuitbreaker.lmsTestRequest.blockingTime", + Long.class, + Constants.SECOND_IN_MILLIS * 20), + environment.getProperty( + "sebserver.webservice.circuitbreaker.lmsTestRequest.timeToRecover", + Long.class, + 0L)); + + this.allQuizzesRequest = asyncService.createCircuitBreaker( + environment.getProperty( + "sebserver.webservice.circuitbreaker.quizzesRequest.attempts", + Integer.class, + 1), + environment.getProperty( + "sebserver.webservice.circuitbreaker.quizzesRequest.blockingTime", + Long.class, + Constants.SECOND_IN_MILLIS * 20), + environment.getProperty( + "sebserver.webservice.circuitbreaker.quizzesRequest.timeToRecover", + Long.class, + 0L)); + + this.quizzesRequest = asyncService.createCircuitBreaker( + environment.getProperty( + "sebserver.webservice.circuitbreaker.quizzesRequest.attempts", + Integer.class, + 1), + environment.getProperty( + "sebserver.webservice.circuitbreaker.quizzesRequest.blockingTime", + Long.class, + Constants.SECOND_IN_MILLIS * 10), + environment.getProperty( + "sebserver.webservice.circuitbreaker.quizzesRequest.timeToRecover", + Long.class, + 0L)); + + this.quizRequest = asyncService.createCircuitBreaker( + environment.getProperty( + "sebserver.webservice.circuitbreaker.quizzesRequest.attempts", + Integer.class, + 1), + environment.getProperty( + "sebserver.webservice.circuitbreaker.quizzesRequest.blockingTime", + Long.class, + Constants.SECOND_IN_MILLIS * 10), + environment.getProperty( + "sebserver.webservice.circuitbreaker.quizzesRequest.timeToRecover", + Long.class, + 0L)); + + this.chaptersRequest = asyncService.createCircuitBreaker( + environment.getProperty( + "sebserver.webservice.circuitbreaker.chaptersRequest.attempts", + Integer.class, + 1), + environment.getProperty( + "sebserver.webservice.circuitbreaker.chaptersRequest.blockingTime", + Long.class, + Constants.SECOND_IN_MILLIS * 10), + environment.getProperty( + "sebserver.webservice.circuitbreaker.chaptersRequest.timeToRecover", + Long.class, + 0L)); + + this.accountDetailRequest = asyncService.createCircuitBreaker( + environment.getProperty( + "sebserver.webservice.circuitbreaker.accountDetailRequest.attempts", + Integer.class, + 2), + environment.getProperty( + "sebserver.webservice.circuitbreaker.accountDetailRequest.blockingTime", + Long.class, + Constants.SECOND_IN_MILLIS * 10), + environment.getProperty( + "sebserver.webservice.circuitbreaker.accountDetailRequest.timeToRecover", + Long.class, + 0L)); + + this.restrictionRequest = asyncService.createCircuitBreaker( + environment.getProperty( + "sebserver.webservice.circuitbreaker.sebrestriction.attempts", + Integer.class, + 1), + environment.getProperty( + "sebserver.webservice.circuitbreaker.sebrestriction.blockingTime", + Long.class, + Constants.SECOND_IN_MILLIS * 10), + environment.getProperty( + "sebserver.webservice.circuitbreaker.sebrestriction.timeToRecover", + Long.class, + 0L)); + + this.releaseRestrictionRequest = asyncService.createCircuitBreaker( + environment.getProperty( + "sebserver.webservice.circuitbreaker.sebrestriction.attempts", + Integer.class, + 2), + environment.getProperty( + "sebserver.webservice.circuitbreaker.sebrestriction.blockingTime", + Long.class, + Constants.SECOND_IN_MILLIS * 10), + environment.getProperty( + "sebserver.webservice.circuitbreaker.sebrestriction.timeToRecover", + Long.class, + 0L)); + } + + @Override + public LmsType getType() { + return this.lmsSetup().getLmsType(); + } + + @Override + public LmsSetup lmsSetup() { + return this.apiTemplateDataSupplier.getLmsSetup(); + } + + @Override + public void checkCourseAPIAccess() { + this.lmsTestRequest + .protectedRun(() -> { + final LmsSetupTestResult testCourseAccessAPI = this.courseAccessAPI.testCourseAccessAPI(); + if (!testCourseAccessAPI.isOk()) { + throw new RuntimeException("No course API Access: " + testCourseAccessAPI); + } + return testCourseAccessAPI; + }); + } + + @Override + public LmsSetupTestResult testCourseAccessAPI() { + if (this.courseAccessAPI != null) { + if (log.isDebugEnabled()) { + log.debug("Test Course Access API for LMSSetup: {}", lmsSetup()); + } + + return this.lmsTestRequest.protectedRun(() -> this.courseAccessAPI.testCourseAccessAPI()) + .onError(error -> log.error( + "Failed to run protectedQuizzesRequest: {}", + error.getMessage())) + .getOrThrow(); + } + + return LmsSetupTestResult.ofAPINotSupported(getType()); + } + + @Override + public FetchStatus getFetchStatus() { + if (this.courseAccessAPI == null) { + return FetchStatus.FETCH_ERROR; + } + + if (this.allQuizzesRequest.getState() != State.CLOSED) { + return FetchStatus.FETCH_ERROR; + } + + return this.courseAccessAPI.getFetchStatus(); + } + + @Override + public Result> getQuizzes(final FilterMap filterMap) { + + if (this.courseAccessAPI == null) { + return Result + .ofError(new UnsupportedOperationException("Course API Not Supported For: " + getType().name())); + } + + if (log.isDebugEnabled()) { + log.debug("Get quizzes for LMSSetup: {}", lmsSetup()); + } + + return this.allQuizzesRequest.protectedRun(() -> this.courseAccessAPI + .getQuizzes(filterMap) + .onError(error -> log.error( + "Failed to run protectedQuizzesRequest: {}", + error.getMessage())) + .getOrThrow()); + } + + @Override + public Result> getQuizzes(final Set ids) { + + if (this.courseAccessAPI == null) { + return Result + .ofError(new UnsupportedOperationException("Course API Not Supported For: " + getType().name())); + } + + if (log.isDebugEnabled()) { + log.debug("Get quizzes {} for LMSSetup: {}", ids, lmsSetup()); + } + + return this.quizzesRequest.protectedRun(() -> this.courseAccessAPI + .getQuizzes(ids) + .onError(error -> log.error( + "Failed to run protectedQuizzesRequest: {}", + error.getMessage())) + .getOrThrow()); + } + + @Override + public Result getQuiz(final String id) { + + if (this.courseAccessAPI == null) { + return Result + .ofError(new UnsupportedOperationException("Course API Not Supported For: " + getType().name())); + } + + if (log.isDebugEnabled()) { + log.debug("Get quiz {} for LMSSetup: {}", id, lmsSetup()); + } + + return this.quizRequest.protectedRun(() -> this.courseAccessAPI + .getQuiz(id) + .onError(error -> log.error( + "Failed to run protectedQuizRequest: {}", + error.getMessage())) + .getOrThrow()); + } + + @Override + public void clearCourseCache() { + if (this.courseAccessAPI != null) { + + if (log.isDebugEnabled()) { + log.debug("Clear course cache for LMSSetup: {}", lmsSetup()); + } + + this.courseAccessAPI.clearCourseCache(); + } + } + + @Override + public Result getExamineeAccountDetails(final String examineeUserId) { + + if (this.courseAccessAPI == null) { + return Result + .ofError(new UnsupportedOperationException("Course API Not Supported For: " + getType().name())); + } + + if (log.isDebugEnabled()) { + log.debug("Get examinee details {} for LMSSetup: {}", examineeUserId, lmsSetup()); + } + + return this.accountDetailRequest.protectedRun(() -> this.courseAccessAPI + .getExamineeAccountDetails(examineeUserId) + .onError(error -> log.error( + "Unexpected error while trying to get examinee account details: {}", + error.getMessage())) + .getOrThrow()); + } + + @Override + public String getExamineeName(final String examineeUserId) { + + if (this.courseAccessAPI == null) { + throw new UnsupportedOperationException("Course API Not Supported For: " + getType().name()); + } + + if (log.isDebugEnabled()) { + log.debug("Get examinee name {} for LMSSetup: {}", examineeUserId, lmsSetup()); + } + + return this.courseAccessAPI.getExamineeName(examineeUserId); + } + + @Override + public Result getCourseChapters(final String courseId) { + + if (this.courseAccessAPI == null) { + return Result + .ofError(new UnsupportedOperationException("Course API Not Supported For: " + getType().name())); + } + + if (log.isDebugEnabled()) { + log.debug("Get course chapters {} for LMSSetup: {}", courseId, lmsSetup()); + } + + return this.chaptersRequest.protectedRun(() -> this.courseAccessAPI + .getCourseChapters(courseId) + .onError(error -> log.error( + "Failed to run getCourseChapters: {}", + error.getMessage())) + .getOrThrow()); + } + + @Override + public LmsSetupTestResult testCourseRestrictionAPI() { + if (this.sebRestrictionAPI != null) { + return this.sebRestrictionAPI.testCourseRestrictionAPI(); + } + + if (log.isDebugEnabled()) { + log.debug("Test course restriction API for LMSSetup: {}", lmsSetup()); + } + + return LmsSetupTestResult.ofAPINotSupported(getType()); + } + + @Override + public Result getSEBClientRestriction(final Exam exam) { + + if (this.sebRestrictionAPI == null) { + return Result.ofError( + new UnsupportedOperationException("SEB Restriction API Not Supported For: " + getType().name())); + } + + if (log.isDebugEnabled()) { + log.debug("Get course restriction: {} for LMSSetup: {}", exam.externalId, lmsSetup()); + } + + return this.restrictionRequest.protectedRun(() -> this.sebRestrictionAPI + .getSEBClientRestriction(exam) + .onError(error -> { + if (error instanceof NoSEBRestrictionException) { + return; + } + log.error("Failed to get SEB restrictions: {}", error.getMessage()); + }) + .getOrThrow()); + } + + @Override + public boolean hasSEBClientRestriction(final Exam exam) { + final Result sebClientRestriction = getSEBClientRestriction(exam); + if (sebClientRestriction.hasError()) { + return false; + } + + final SEBRestriction sebRestriction = sebClientRestriction.get(); + return !sebRestriction.configKeys.isEmpty() || !sebRestriction.browserExamKeys.isEmpty(); + } + + @Override + public Result applySEBClientRestriction( + final String externalExamId, + final SEBRestriction sebRestrictionData) { + + if (this.sebRestrictionAPI == null) { + return Result.ofError( + new UnsupportedOperationException("SEB Restriction API Not Supported For: " + getType().name())); + } + + if (log.isDebugEnabled()) { + log.debug("Apply course restriction: {} for LMSSetup: {}", externalExamId, lmsSetup()); + } + + return this.restrictionRequest.protectedRun(() -> this.sebRestrictionAPI + .applySEBClientRestriction(externalExamId, sebRestrictionData) + .onError(error -> log.error( + "Failed to apply SEB restrictions: {}", + error.getMessage())) + .getOrThrow()); + } + + @Override + public Result releaseSEBClientRestriction(final Exam exam) { + + if (this.sebRestrictionAPI == null) { + return Result.ofError( + new UnsupportedOperationException("SEB Restriction API Not Supported For: " + getType().name())); + } + + if (log.isDebugEnabled()) { + log.debug("Release course restriction: {} for LMSSetup: {}", exam.externalId, lmsSetup()); + } + + return this.releaseRestrictionRequest.protectedRun(() -> this.sebRestrictionAPI + .releaseSEBClientRestriction(exam) + .onError(error -> log.error( + "Failed to release SEB restrictions: {}", + error.getMessage())) + .getOrThrow()); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/SEBRestrictionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/SEBRestrictionServiceImpl.java index bfc767d2..05c69b7f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/SEBRestrictionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/SEBRestrictionServiceImpl.java @@ -22,6 +22,7 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -38,6 +39,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ExamConfigService; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamFinishedEvent; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamStartedEvent; @Lazy @Service @@ -153,7 +156,7 @@ public class SEBRestrictionServiceImpl implements SEBRestrictionService { final Collection browserExamKeys = sebRestriction.getBrowserExamKeys(); final Exam newExam = new Exam( exam.id, - null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, exam.supporter, exam.status, null, @@ -188,6 +191,30 @@ public class SEBRestrictionServiceImpl implements SEBRestrictionService { .flatMap(this.examDAO::byPK); } + @EventListener + public void notifyExamStarted(final ExamStartedEvent event) { + + log.info("ExamStartedEvent received, process applySEBClientRestriction..."); + + applySEBClientRestriction(event.exam) + .onError(error -> log.error( + "Failed to apply SEB restrictions for started exam: {}", + event.exam, + error)); + } + + @EventListener + public void notifyExamFinished(final ExamFinishedEvent event) { + + log.info("ExamFinishedEvent received, process releaseSEBClientRestriction..."); + + releaseSEBClientRestriction(event.exam) + .onError(error -> log.error( + "Failed to release SEB restrictions for finished exam: {}", + event.exam, + error)); + } + @Override public Result applySEBClientRestriction(final Exam exam) { return Result.tryCatch(() -> { @@ -221,13 +248,19 @@ public class SEBRestrictionServiceImpl implements SEBRestrictionService { @Override public Result releaseSEBClientRestriction(final Exam exam) { - if (log.isDebugEnabled()) { - log.debug(" *** SEB Restriction *** Release SEB Client restrictions from LMS for exam: {}", exam); - } - return this.lmsAPIService .getLmsAPITemplate(exam.lmsSetupId) - .flatMap(template -> template.releaseSEBClientRestriction(exam)); + .map(template -> { + if (template.lmsSetup().lmsType.features.contains(Features.SEB_RESTRICTION)) { + if (log.isDebugEnabled()) { + log.debug(" *** SEB Restriction *** Release SEB Client restrictions from LMS for exam: {}", + exam); + } + template.releaseSEBClientRestriction(exam); + + } + return exam; + }); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/ans/AnsLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/ans/AnsLmsAPITemplate.java index 721a2c18..36a51636 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/ans/AnsLmsAPITemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/ans/AnsLmsAPITemplate.java @@ -18,7 +18,6 @@ import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -28,7 +27,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.CacheManager; import org.springframework.core.ParameterizedTypeReference; -import org.springframework.core.env.Environment; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -39,7 +37,6 @@ import org.springframework.web.client.RestTemplate; import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService; import ch.ethz.seb.sebserver.gbl.api.APIMessage; -import ch.ethz.seb.sebserver.gbl.async.AsyncService; import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService; import ch.ethz.seb.sebserver.gbl.client.ClientCredentials; import ch.ethz.seb.sebserver.gbl.client.ProxyData; @@ -78,11 +75,9 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms final ClientHttpRequestFactoryService clientHttpRequestFactoryService, final ClientCredentialService clientCredentialService, final APITemplateDataSupplier apiTemplateDataSupplier, - final AsyncService asyncService, - final Environment environment, final CacheManager cacheManager) { - super(asyncService, environment, cacheManager); + super(cacheManager); this.clientHttpRequestFactoryService = clientHttpRequestFactoryService; this.clientCredentialService = clientCredentialService; @@ -170,7 +165,7 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms @Override public Result> getQuizzes(final FilterMap filterMap) { return this - .protectedQuizzesRequest(filterMap) + .allQuizzesRequest(filterMap) .map(quizzes -> quizzes.stream() .filter(LmsAPIService.quizFilterPredicate(filterMap)) .collect(Collectors.toList())); @@ -191,7 +186,7 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms }); if (!leftIds.isEmpty()) { - result.addAll(super.protectedQuizzesRequest(leftIds).getOrThrow()); + result.addAll(quizzesRequest(leftIds).getOrThrow()); } return result; @@ -205,7 +200,7 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms return Result.of(fromCache); } - return super.protectedQuizRequest(id); + return quizRequest(id); } private List collectAllQuizzes(final AnsPersonalRestTemplate restTemplate) { @@ -279,33 +274,28 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms .collect(Collectors.toList()); } - @Override - protected Supplier> allQuizzesSupplier(final FilterMap filterMap) { + protected Result> allQuizzesRequest(final FilterMap filterMap) { // We cannot filter by from-date or partial names using the Ans search API. // Only exact matches are permitted. So we're not implementing filtering // on the API level and always retrieve all assignments and let SEB server // do the filtering. - return () -> { + return Result.tryCatch(() -> { final List res = getRestTemplate() .map(this::collectAllQuizzes) .getOrThrow(); super.putToCache(res); return res; - }; + }); } - @Override - protected Supplier> quizzesSupplier(final Set ids) { - return () -> getRestTemplate() - .map(t -> this.getQuizzesByIds(t, ids)) - .getOrThrow(); + protected Result> quizzesRequest(final Set ids) { + return getRestTemplate() + .map(t -> this.getQuizzesByIds(t, ids)); } - @Override - protected Supplier quizSupplier(final String id) { - return () -> getRestTemplate() - .map(t -> this.getQuizByAssignmentId(t, id)) - .getOrThrow(); + protected Result quizRequest(final String id) { + return getRestTemplate() + .map(t -> this.getQuizByAssignmentId(t, id)); } private ExamineeAccountDetails getExamineeById(final RestTemplate restTemplate, final String id) { @@ -324,17 +314,21 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms } @Override - protected Supplier accountDetailsSupplier(final String id) { - return () -> getRestTemplate() - .map(t -> this.getExamineeById(t, id)) - .getOrThrow(); + public Result getExamineeAccountDetails(final String examineeUserId) { + return getRestTemplate().map(t -> this.getExamineeById(t, examineeUserId)); } @Override - protected Supplier getCourseChaptersSupplier(final String courseId) { - return () -> { - throw new UnsupportedOperationException("not available yet"); - }; + public String getExamineeName(final String examineeUserId) { + return getExamineeAccountDetails(examineeUserId) + .map(ExamineeAccountDetails::getDisplayName) + .onError(error -> log.warn("Failed to request user-name for ID: {}", error.getMessage(), error)) + .getOr(examineeUserId); + } + + @Override + public Result getCourseChapters(final String courseId) { + return Result.ofError(new UnsupportedOperationException("not available yet")); } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/ans/AnsLmsAPITemplateFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/ans/AnsLmsAPITemplateFactory.java index 549bf5a5..d0fc5385 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/ans/AnsLmsAPITemplateFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/ans/AnsLmsAPITemplateFactory.java @@ -22,6 +22,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplateFactory; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsAPITemplateAdapter; @Lazy @Service @@ -63,13 +64,17 @@ public class AnsLmsAPITemplateFactory implements LmsAPITemplateFactory { @Override public Result create(final APITemplateDataSupplier apiTemplateDataSupplier) { return Result.tryCatch(() -> { - return new AnsLmsAPITemplate( + final AnsLmsAPITemplate ansLmsAPITemplate = new AnsLmsAPITemplate( this.clientHttpRequestFactoryService, this.clientCredentialService, apiTemplateDataSupplier, + this.cacheManager); + return new LmsAPITemplateAdapter( this.asyncService, this.environment, - this.cacheManager); + apiTemplateDataSupplier, + ansLmsAPITemplate, + ansLmsAPITemplate); }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseAccess.java index fd4165b6..57d2f0a3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseAccess.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseAccess.java @@ -17,14 +17,12 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.CacheManager; -import org.springframework.core.env.Environment; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -45,7 +43,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; -import ch.ethz.seb.sebserver.gbl.async.AsyncService; import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; @@ -57,12 +54,13 @@ import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.CourseAccessAPI; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCachedCourseAccess; /** Implements the LmsAPITemplate for Open edX LMS Course API access. * * See also: https://course-catalog-api-guide.readthedocs.io */ -final class OpenEdxCourseAccess extends AbstractCachedCourseAccess { +final class OpenEdxCourseAccess extends AbstractCachedCourseAccess implements CourseAccessAPI { private static final Logger log = LoggerFactory.getLogger(OpenEdxCourseAccess.class); @@ -84,11 +82,9 @@ final class OpenEdxCourseAccess extends AbstractCachedCourseAccess { final JSONMapper jsonMapper, final OpenEdxRestTemplateFactory openEdxRestTemplateFactory, final WebserviceInfo webserviceInfo, - final AsyncService asyncService, - final Environment environment, final CacheManager cacheManager) { - super(asyncService, environment, cacheManager); + super(cacheManager); this.jsonMapper = jsonMapper; this.openEdxRestTemplateFactory = openEdxRestTemplateFactory; this.webserviceInfo = webserviceInfo; @@ -104,7 +100,8 @@ final class OpenEdxCourseAccess extends AbstractCachedCourseAccess { return this.lmsSetupId; } - LmsSetupTestResult initAPIAccess() { + @Override + public LmsSetupTestResult testCourseAccessAPI() { final LmsSetupTestResult attributesCheck = this.openEdxRestTemplateFactory.test(); if (!attributesCheck.isOk()) { @@ -141,86 +138,38 @@ final class OpenEdxCourseAccess extends AbstractCachedCourseAccess { } @Override - protected Supplier accountDetailsSupplier(final String examineeSessionId) { - return () -> { - try { - final LmsSetup lmsSetup = getApiTemplateDataSupplier().getLmsSetup(); - final HttpHeaders httpHeaders = new HttpHeaders(); - final OAuth2RestTemplate template = getRestTemplate() - .getOrThrow(); + public Result> getQuizzes(final FilterMap filterMap) { + return getRestTemplate().map(this::collectAllQuizzes); + } - final String externalStartURI = this.webserviceInfo - .getLmsExternalAddressAlias(lmsSetup.lmsApiUrl); - - final String uri = (externalStartURI != null) - ? externalStartURI + OPEN_EDX_DEFAULT_USER_PROFILE_ENDPOINT + examineeSessionId - : lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_USER_PROFILE_ENDPOINT + examineeSessionId; - - final String responseJSON = template.exchange( - uri, - HttpMethod.GET, - new HttpEntity<>(httpHeaders), - String.class) - .getBody(); - - final EdxUserDetails[] userDetails = this.jsonMapper. readValue( - responseJSON, - new TypeReference() { - }); - - if (userDetails == null || userDetails.length <= 0) { - throw new RuntimeException("No user details on Open edX API request"); - } - - final Map additionalAttributes = new HashMap<>(); - additionalAttributes.put("bio", userDetails[0].bio); - additionalAttributes.put("country", userDetails[0].country); - additionalAttributes.put("date_joined", userDetails[0].date_joined); - additionalAttributes.put("gender", userDetails[0].gender); - additionalAttributes.put("is_active", String.valueOf(userDetails[0].is_active)); - additionalAttributes.put("mailing_address", userDetails[0].mailing_address); - additionalAttributes.put("secondary_email", userDetails[0].secondary_email); - - return new ExamineeAccountDetails( - userDetails[0].username, - userDetails[0].name, - userDetails[0].username, - userDetails[0].email, - additionalAttributes); - } catch (final Exception e) { - throw new RuntimeException(e); + @Override + public Result getQuiz(final String id) { + return Result.tryCatch(() -> { + final QuizData fromCache = super.getFromCache(id); + if (fromCache != null) { + return fromCache; } - }; - } - @Override - protected Supplier> allQuizzesSupplier(final FilterMap filterMap) { - return () -> getRestTemplate() - .map(this::collectAllQuizzes) - .getOrThrow(); - } - - @Override - protected Supplier quizSupplier(final String id) { - return () -> { final LmsSetup lmsSetup = getApiTemplateDataSupplier().getLmsSetup(); final String externalStartURI = getExternalLMSServerAddress(lmsSetup); - final QuizData quizData = quizDataOf( - lmsSetup, - this.getOneCourse(id, this.restTemplate, id), - externalStartURI); + final QuizData quizData = getRestTemplate() + .map(template -> quizDataOf( + lmsSetup, + this.getOneCourse(id, template, id), + externalStartURI)) + .getOrThrow(); if (quizData != null) { super.putToCache(quizData); } return quizData; - }; + }); } @Override - protected Supplier> quizzesSupplier(final Set ids) { + public Result> getQuizzes(final Set ids) { if (ids.size() == 1) { - return () -> { + return Result.tryCatch(() -> { final String id = ids.iterator().next(); @@ -239,17 +188,73 @@ final class OpenEdxCourseAccess extends AbstractCachedCourseAccess { getRestTemplate().getOrThrow(), id), externalStartURI)); - }; + }); } else { - return () -> getRestTemplate() - .map(template -> this.collectQuizzes(template, ids)) - .getOrThrow(); + return getRestTemplate().map(template -> this.collectQuizzes(template, ids)); } } @Override - protected Supplier getCourseChaptersSupplier(final String courseId) { - return () -> { + public Result getExamineeAccountDetails(final String examineeUserId) { + return Result.tryCatch(() -> { + + final LmsSetup lmsSetup = getApiTemplateDataSupplier().getLmsSetup(); + final HttpHeaders httpHeaders = new HttpHeaders(); + final OAuth2RestTemplate template = getRestTemplate() + .getOrThrow(); + + final String externalStartURI = this.webserviceInfo + .getLmsExternalAddressAlias(lmsSetup.lmsApiUrl); + + final String uri = (externalStartURI != null) + ? externalStartURI + OPEN_EDX_DEFAULT_USER_PROFILE_ENDPOINT + examineeUserId + : lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_USER_PROFILE_ENDPOINT + examineeUserId; + + final String responseJSON = template.exchange( + uri, + HttpMethod.GET, + new HttpEntity<>(httpHeaders), + String.class) + .getBody(); + + final EdxUserDetails[] userDetails = this.jsonMapper. readValue( + responseJSON, + new TypeReference() { + }); + + if (userDetails == null || userDetails.length <= 0) { + throw new RuntimeException("No user details on Open edX API request"); + } + + final Map additionalAttributes = new HashMap<>(); + additionalAttributes.put("bio", userDetails[0].bio); + additionalAttributes.put("country", userDetails[0].country); + additionalAttributes.put("date_joined", userDetails[0].date_joined); + additionalAttributes.put("gender", userDetails[0].gender); + additionalAttributes.put("is_active", String.valueOf(userDetails[0].is_active)); + additionalAttributes.put("mailing_address", userDetails[0].mailing_address); + additionalAttributes.put("secondary_email", userDetails[0].secondary_email); + + return new ExamineeAccountDetails( + userDetails[0].username, + userDetails[0].name, + userDetails[0].username, + userDetails[0].email, + additionalAttributes); + }); + } + + @Override + public String getExamineeName(final String examineeUserId) { + return getExamineeAccountDetails(examineeUserId) + .map(ExamineeAccountDetails::getDisplayName) + .onError(error -> log.warn("Failed to request user-name for ID: {}", error.getMessage(), error)) + .getOr(examineeUserId); + } + + @Override + public Result getCourseChapters(final String courseId) { + return Result.tryCatch(() -> { final LmsSetup lmsSetup = getApiTemplateDataSupplier().getLmsSetup(); final String uri = @@ -262,7 +267,7 @@ final class OpenEdxCourseAccess extends AbstractCachedCourseAccess { .filter(block -> OPEN_EDX_DEFAULT_BLOCKS_TYPE_CHAPTER.equals(block.type)) .map(block -> new Chapters.Chapter(block.display_name, block.block_id)) .collect(Collectors.toList())); - }; + }); } public Result> getQuizzesFromCache(final Set ids) { @@ -279,7 +284,7 @@ final class OpenEdxCourseAccess extends AbstractCachedCourseAccess { }); if (!leftIds.isEmpty()) { - result.addAll(super.protectedQuizzesRequest(leftIds).getOrThrow()); + result.addAll(getQuizzes(leftIds).getOrThrow()); } return result; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseRestriction.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseRestriction.java index 8ba22c7d..b6c16975 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseRestriction.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseRestriction.java @@ -22,17 +22,19 @@ import org.springframework.web.client.HttpClientErrorException; import com.fasterxml.jackson.core.JsonProcessingException; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSEBRestriction; +import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; 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.SEBRestrictionAPI; /** The open edX SEB course restriction API implementation. * * See also : https://seb-openedx.readthedocs.io/en/latest/ */ -public class OpenEdxCourseRestriction { +public class OpenEdxCourseRestriction implements SEBRestrictionAPI { private static final Logger log = LoggerFactory.getLogger(OpenEdxCourseRestriction.class); @@ -54,7 +56,8 @@ public class OpenEdxCourseRestriction { this.openEdxRestTemplateFactory = openEdxRestTemplateFactory; } - LmsSetupTestResult initAPIAccess() { + @Override + public LmsSetupTestResult testCourseRestrictionAPI() { final LmsSetupTestResult attributesCheck = this.openEdxRestTemplateFactory.test(); if (!attributesCheck.isOk()) { @@ -76,8 +79,6 @@ public class OpenEdxCourseRestriction { // not accessible within OAuth2 authentication (just with user - authentication), // we can only check if the endpoint is available for now. This is checked // if there is no 404 response. - // TODO: Ask eduNEXT to implement also OAuth2 API access for this endpoint to be able - // to check the version of the installed plugin. final LmsSetup lmsSetup = this.openEdxRestTemplateFactory.apiTemplateDataSupplier.getLmsSetup(); final String url = lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_COURSE_RESTRICTION_API_INFO; @@ -95,21 +96,22 @@ public class OpenEdxCourseRestriction { } if (log.isDebugEnabled()) { - log.debug("Sucessfully checked SEB Open edX integration Plugin"); + log.debug("Successfully checked SEB Open edX integration Plugin"); } } return LmsSetupTestResult.ofOkay(LmsType.OPEN_EDX); } - Result getSEBRestriction(final String courseId) { + @Override + public Result getSEBClientRestriction(final Exam exam) { if (log.isDebugEnabled()) { - log.debug("GET SEB Client restriction on course: {}", courseId); + log.debug("GET SEB Client restriction on exam: {}", exam); } + final String courseId = exam.externalId; final LmsSetup lmsSetup = this.openEdxRestTemplateFactory.apiTemplateDataSupplier.getLmsSetup(); - return Result.tryCatch(() -> { final String url = lmsSetup.lmsApiUrl + getSEBRestrictionUrl(courseId); final HttpHeaders httpHeaders = new HttpHeaders(); @@ -127,27 +129,29 @@ public class OpenEdxCourseRestriction { if (log.isDebugEnabled()) { log.debug("Successfully GET SEB Client restriction on course: {}", courseId); } - return data; + return SEBRestriction.from(exam.id, data); } catch (final HttpClientErrorException ce) { - if (ce.getStatusCode() == HttpStatus.NOT_FOUND || ce.getStatusCode() == HttpStatus.UNAUTHORIZED) { - throw new NoSEBRestrictionException(ce); + if (ce.getStatusCode() == HttpStatus.NOT_FOUND) { + // No SEB restriction is set for the specified exam, return an empty one + return new SEBRestriction(exam.id, null, null, null); } throw ce; } }); } - Result putSEBRestriction( - final String courseId, - final OpenEdxSEBRestriction restriction) { + @Override + public Result applySEBClientRestriction( + final String externalExamId, + final SEBRestriction sebRestrictionData) { if (log.isDebugEnabled()) { - log.debug("PUT SEB Client restriction on course: {} : {}", courseId, restriction); + log.debug("PUT SEB Client restriction on course: {} : {}", externalExamId, sebRestrictionData); } return Result.tryCatch(() -> { final LmsSetup lmsSetup = this.openEdxRestTemplateFactory.apiTemplateDataSupplier.getLmsSetup(); - final String url = lmsSetup.lmsApiUrl + getSEBRestrictionUrl(courseId); + final String url = lmsSetup.lmsApiUrl + getSEBRestrictionUrl(externalExamId); final HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); httpHeaders.add(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate"); @@ -157,24 +161,26 @@ public class OpenEdxCourseRestriction { .exchange( url, HttpMethod.PUT, - new HttpEntity<>(toJson(restriction), httpHeaders), + new HttpEntity<>(toJson(OpenEdxSEBRestriction.from(sebRestrictionData)), httpHeaders), OpenEdxSEBRestriction.class) .getBody(); if (log.isDebugEnabled()) { - log.debug("Successfully PUT SEB Client restriction on course: {} : {}", courseId, body); + log.debug("Successfully PUT SEB Client restriction on course: {} : {}", externalExamId, body); } - return true; + return sebRestrictionData; }); } - Result deleteSEBRestriction(final String courseId) { + @Override + public Result releaseSEBClientRestriction(final Exam exam) { if (log.isDebugEnabled()) { - log.debug("DELETE SEB Client restriction on course: {}", courseId); + log.debug("DELETE SEB Client restriction on exam: {}", exam); } + final String courseId = exam.externalId; return Result.tryCatch(() -> { final LmsSetup lmsSetup = this.openEdxRestTemplateFactory.apiTemplateDataSupplier.getLmsSetup(); final String url = lmsSetup.lmsApiUrl + getSEBRestrictionUrl(courseId); @@ -193,7 +199,7 @@ public class OpenEdxCourseRestriction { if (log.isDebugEnabled()) { log.debug("Successfully PUT SEB Client restriction on course: {}", courseId); } - return true; + return exam; } else { throw new RuntimeException("Unexpected response for deletion: " + exchange); } @@ -201,74 +207,6 @@ public class OpenEdxCourseRestriction { } -// private BooleanSupplier pushSEBRestrictionFunction( -// final OpenEdxSEBRestriction restriction, -// final String courseId) { -// -// final LmsSetup lmsSetup = this.openEdxRestTemplateFactory.apiTemplateDataSupplier.getLmsSetup(); -// final String url = lmsSetup.lmsApiUrl + getSEBRestrictionUrl(courseId); -// final HttpHeaders httpHeaders = new HttpHeaders(); -// httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); -// httpHeaders.add(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate"); -// return () -> { -// final OpenEdxSEBRestriction body = this.restTemplate.exchange( -// url, -// HttpMethod.PUT, -// new HttpEntity<>(toJson(restriction), httpHeaders), -// OpenEdxSEBRestriction.class) -// .getBody(); -// -// if (log.isDebugEnabled()) { -// log.debug("Successfully PUT SEB Client restriction on course: {} : {}", courseId, body); -// } -// -// return true; -// }; -// } - -// private BooleanSupplier deleteSEBRestrictionFunction(final String courseId) { -// -// final LmsSetup lmsSetup = this.openEdxRestTemplateFactory.apiTemplateDataSupplier.getLmsSetup(); -// final String url = lmsSetup.lmsApiUrl + getSEBRestrictionUrl(courseId); -// return () -> { -// final HttpHeaders httpHeaders = new HttpHeaders(); -// httpHeaders.add(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate"); -// final ResponseEntity exchange = this.restTemplate.exchange( -// url, -// HttpMethod.DELETE, -// new HttpEntity<>(httpHeaders), -// Object.class); -// -// if (exchange.getStatusCode() == HttpStatus.NO_CONTENT) { -// if (log.isDebugEnabled()) { -// log.debug("Successfully PUT SEB Client restriction on course: {}", courseId); -// } -// } else { -// log.error("Unexpected response for deletion: {}", exchange); -// return false; -// } -// -// return true; -// }; -// } - -// private Result handleSEBRestriction(final BooleanSupplier task) { -// return getRestTemplate() -// .map(restTemplate -> { -// try { -// return task.getAsBoolean(); -// } catch (final HttpClientErrorException ce) { -// if (ce.getStatusCode() == HttpStatus.UNAUTHORIZED) { -// throw new APIMessageException(APIMessage.ErrorMessage.UNAUTHORIZED.of(ce.getMessage() -// + " Unable to get access for API. Please check the corresponding LMS Setup ")); -// } -// throw ce; -// } catch (final Exception e) { -// throw new RuntimeException("Unexpected: ", e); -// } -// }); -// } - private String getSEBRestrictionUrl(final String courseId) { return String.format(OPEN_EDX_DEFAULT_COURSE_RESTRICTION_API_PATH, courseId); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxLmsAPITemplate.java deleted file mode 100644 index 8975f97a..00000000 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxLmsAPITemplate.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.edx; - -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; -import ch.ethz.seb.sebserver.gbl.model.exam.Exam; -import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSEBRestriction; -import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; -import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; -import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; -import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; -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.dao.FilterMap; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; - -/** The OpenEdxLmsAPITemplate is separated into two parts: - * - OpenEdxCourseAccess implements the course access API - * - OpenEdxCourseRestriction implements the SEB restriction API - * - Both uses the OpenEdxRestTemplateFactory to create a spring based RestTemplate to access the LMS API */ -final class OpenEdxLmsAPITemplate implements LmsAPITemplate { - - private static final Logger log = LoggerFactory.getLogger(OpenEdxLmsAPITemplate.class); - - private final OpenEdxCourseAccess openEdxCourseAccess; - private final OpenEdxCourseRestriction openEdxCourseRestriction; - - OpenEdxLmsAPITemplate( - final OpenEdxCourseAccess openEdxCourseAccess, - final OpenEdxCourseRestriction openEdxCourseRestriction) { - - this.openEdxCourseAccess = openEdxCourseAccess; - this.openEdxCourseRestriction = openEdxCourseRestriction; - } - - @Override - public LmsType getType() { - return LmsType.OPEN_EDX; - } - - @Override - public LmsSetup lmsSetup() { - return this.openEdxCourseAccess - .getApiTemplateDataSupplier() - .getLmsSetup(); - } - - @Override - public LmsSetupTestResult testCourseAccessAPI() { - return this.openEdxCourseAccess.initAPIAccess(); - } - - @Override - public LmsSetupTestResult testCourseRestrictionAPI() { - return this.openEdxCourseRestriction.initAPIAccess(); - } - - @Override - public Result> getQuizzes(final FilterMap filterMap) { - return this.openEdxCourseAccess - .protectedQuizzesRequest(filterMap) - .map(quizzes -> quizzes.stream() - .filter(LmsAPIService.quizFilterPredicate(filterMap)) - .collect(Collectors.toList())); - } - - @Override - public Result getQuiz(final String id) { - final QuizData quizFromCache = this.openEdxCourseAccess.getQuizFromCache(id); - if (quizFromCache != null) { - return Result.of(quizFromCache); - } - - return this.openEdxCourseAccess.protectedQuizRequest(id); - } - - @Override - public Result> getQuizzes(final Set ids) { - return this.openEdxCourseAccess.getQuizzesFromCache(ids); - } - - @Override - public void clearCache() { - this.openEdxCourseAccess.clearCache(); - } - - @Override - public Result getCourseChapters(final String courseId) { - return Result.tryCatch(() -> this.openEdxCourseAccess - .getCourseChaptersSupplier(courseId) - .get()); - } - - @Override - public Result getExamineeAccountDetails(final String examineeSessionId) { - return this.openEdxCourseAccess.getExamineeAccountDetails(examineeSessionId); - } - - @Override - public String getExamineeName(final String examineeSessionId) { - return this.openEdxCourseAccess.getExamineeName(examineeSessionId); - } - - @Override - public Result getSEBClientRestriction(final Exam exam) { - if (log.isDebugEnabled()) { - log.debug("Get SEB Client restriction for Exam: {}", exam); - } - - return this.openEdxCourseRestriction - .getSEBRestriction(exam.externalId) - .map(restriction -> SEBRestriction.from(exam.id, restriction)); - } - - @Override - public Result applySEBClientRestriction( - final String externalExamId, - final SEBRestriction sebRestrictionData) { - - if (log.isDebugEnabled()) { - log.debug("Apply SEB Client restriction: {}", sebRestrictionData); - } - - return this.openEdxCourseRestriction - .putSEBRestriction( - externalExamId, - OpenEdxSEBRestriction.from(sebRestrictionData)) - .map(result -> sebRestrictionData); - } - - @Override - public Result releaseSEBClientRestriction(final Exam exam) { - - if (log.isDebugEnabled()) { - log.debug("Release SEB Client restriction for Exam: {}", exam); - } - - return this.openEdxCourseRestriction - .deleteSEBRestriction(exam.externalId) - .map(result -> exam); - } - -} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxLmsAPITemplateFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxLmsAPITemplateFactory.java index a3609f16..20b3ea92 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxLmsAPITemplateFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxLmsAPITemplateFactory.java @@ -27,6 +27,7 @@ import ch.ethz.seb.sebserver.webservice.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplateFactory; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsAPITemplateAdapter; @Lazy @Service @@ -87,8 +88,6 @@ public class OpenEdxLmsAPITemplateFactory implements LmsAPITemplateFactory { this.jsonMapper, openEdxRestTemplateFactory, this.webserviceInfo, - this.asyncService, - this.environment, this.cacheManager); final OpenEdxCourseRestriction openEdxCourseRestriction = new OpenEdxCourseRestriction( @@ -96,7 +95,10 @@ public class OpenEdxLmsAPITemplateFactory implements LmsAPITemplateFactory { openEdxRestTemplateFactory, this.restrictionAPIPushCount); - return new OpenEdxLmsAPITemplate( + return new LmsAPITemplateAdapter( + this.asyncService, + this.environment, + apiTemplateDataSupplier, openEdxCourseAccess, openEdxCourseRestriction); }); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockupLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockCourseAccessAPI.java similarity index 68% rename from src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockupLmsAPITemplate.java rename to src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockCourseAccessAPI.java index 0efaf594..37e32ff0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockupLmsAPITemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockCourseAccessAPI.java @@ -1,330 +1,252 @@ -/* - * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.mockup; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.apache.commons.lang3.StringUtils; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.env.Environment; - -import ch.ethz.seb.sebserver.gbl.Constants; -import ch.ethz.seb.sebserver.gbl.api.APIMessage; -import ch.ethz.seb.sebserver.gbl.async.AsyncService; -import ch.ethz.seb.sebserver.gbl.client.ClientCredentials; -import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP; -import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; -import ch.ethz.seb.sebserver.gbl.model.exam.Exam; -import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; -import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; -import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; -import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; -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.WebserviceInfo; -import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCourseAccess; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.NoSEBRestrictionException; - -public class MockupLmsAPITemplate implements LmsAPITemplate { - - private static final Logger log = LoggerFactory.getLogger(MockupLmsAPITemplate.class); - - private final Collection mockups; - private final WebserviceInfo webserviceInfo; - private final APITemplateDataSupplier apiTemplateDataSupplier; - - private final AbstractCourseAccess abstractCourseAccess; - - MockupLmsAPITemplate( - final AsyncService asyncService, - final Environment environment, - final APITemplateDataSupplier apiTemplateDataSupplier, - final WebserviceInfo webserviceInfo) { - - this.apiTemplateDataSupplier = apiTemplateDataSupplier; - this.webserviceInfo = webserviceInfo; - this.mockups = new ArrayList<>(); - - this.abstractCourseAccess = new AbstractCourseAccess(asyncService, environment) { - - @Override - protected Supplier accountDetailsSupplier(final String examineeSessionId) { - return () -> MockupLmsAPITemplate.this - .getExamineeAccountDetails_protected(examineeSessionId) - .getOrThrow(); - } - - @Override - protected Supplier> allQuizzesSupplier(final FilterMap filterMap) { - return () -> MockupLmsAPITemplate.this.getQuizzes_protected(filterMap).getOrThrow(); - } - - @Override - protected Supplier> quizzesSupplier(final Set ids) { - return () -> MockupLmsAPITemplate.this.getQuizzes_protected(ids).getOrThrow(); - } - - @Override - protected Supplier quizSupplier(final String id) { - return () -> MockupLmsAPITemplate.this.getQuiz_protected(id).getOrThrow(); - } - - @Override - protected Supplier getCourseChaptersSupplier(final String courseId) { - throw new UnsupportedOperationException("Course Chapter feature not supported"); - } - - }; - - final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup(); - final Long lmsSetupId = lmsSetup.id; - final Long institutionId = lmsSetup.getInstitutionId(); - final LmsType lmsType = lmsSetup.getLmsType(); - - this.mockups.add(new QuizData( - "quiz1", institutionId, lmsSetupId, lmsType, "Demo Quiz 1 (MOCKUP)", "

Demo Quiz Mockup

", - "2020-01-01T09:00:00Z", null, "http://lms.mockup.com/api/")); - this.mockups.add(new QuizData( - "quiz2", institutionId, lmsSetupId, lmsType, "Demo Quiz 2 (MOCKUP)", "

Demo Quiz Mockup

", - "2020-01-01T09:00:00Z", "2025-01-01T09:00:00Z", "http://lms.mockup.com/api/")); - this.mockups.add(new QuizData( - "quiz3", institutionId, lmsSetupId, lmsType, "Demo Quiz 3 (MOCKUP)", "

Demo Quiz Mockup

", - "2018-07-30T09:00:00Z", "2018-08-01T00:00:00Z", "http://lms.mockup.com/api/")); - this.mockups.add(new QuizData( - "quiz4", institutionId, lmsSetupId, lmsType, "Demo Quiz 4 (MOCKUP)", "

Demo Quiz Mockup

", - "2018-01-01T00:00:00Z", "2025-01-01T00:00:00Z", "http://lms.mockup.com/api/")); - this.mockups.add(new QuizData( - "quiz5", institutionId, lmsSetupId, lmsType, "Demo Quiz 5 (MOCKUP)", "

Demo Quiz Mockup

", - "2018-01-01T09:00:00Z", "2025-01-01T09:00:00Z", "http://lms.mockup.com/api/")); - this.mockups.add(new QuizData( - "quiz6", institutionId, lmsSetupId, lmsType, "Demo Quiz 6 (MOCKUP)", "

Demo Quiz Mockup

", - "2019-01-01T09:00:00Z", "2025-01-01T09:00:00Z", "http://lms.mockup.com/api/")); - this.mockups.add(new QuizData( - "quiz7", institutionId, lmsSetupId, lmsType, "Demo Quiz 7 (MOCKUP)", "

Demo Quiz Mockup

", - "2018-01-01T09:00:00Z", "2025-01-01T09:00:00Z", "http://lms.mockup.com/api/")); - - this.mockups.add(new QuizData( - "quiz10", institutionId, lmsSetupId, lmsType, "Demo Quiz 10 (MOCKUP)", - "Starts in a minute and ends after five minutes", - DateTime.now(DateTimeZone.UTC).plus(Constants.MINUTE_IN_MILLIS) - .toString(Constants.DEFAULT_DATE_TIME_FORMAT), - DateTime.now(DateTimeZone.UTC).plus(6 * Constants.MINUTE_IN_MILLIS) - .toString(Constants.DEFAULT_DATE_TIME_FORMAT), - "http://lms.mockup.com/api/")); - this.mockups.add(new QuizData( - "quiz11", institutionId, lmsSetupId, lmsType, "Demo Quiz 11 (MOCKUP)", - "Starts in a minute and ends never", - DateTime.now(DateTimeZone.UTC).plus(Constants.MINUTE_IN_MILLIS) - .toString(Constants.DEFAULT_DATE_TIME_FORMAT), - null, - "http://lms.mockup.com/api/")); - } - - @Override - public LmsType getType() { - return LmsType.MOCKUP; - } - - @Override - public LmsSetup lmsSetup() { - return this.apiTemplateDataSupplier.getLmsSetup(); - } - - private List checkAttributes() { - final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup(); - final ClientCredentials lmsClientCredentials = this.apiTemplateDataSupplier.getLmsClientCredentials(); - final List missingAttrs = new ArrayList<>(); - if (StringUtils.isBlank(lmsSetup.lmsApiUrl)) { - missingAttrs.add(APIMessage.fieldValidationError( - LMS_SETUP.ATTR_LMS_URL, - "lmsSetup:lmsUrl:notNull")); - } - if (!lmsClientCredentials.hasClientId()) { - missingAttrs.add(APIMessage.fieldValidationError( - LMS_SETUP.ATTR_LMS_CLIENTNAME, - "lmsSetup:lmsClientname:notNull")); - } - if (!lmsClientCredentials.hasSecret()) { - missingAttrs.add(APIMessage.fieldValidationError( - LMS_SETUP.ATTR_LMS_CLIENTSECRET, - "lmsSetup:lmsClientsecret:notNull")); - } - return missingAttrs; - } - - @Override - public LmsSetupTestResult testCourseAccessAPI() { - log.info("Test Lms Binding for Mockup and LmsSetup: {}", this.apiTemplateDataSupplier.getLmsSetup()); - - final List missingAttrs = checkAttributes(); - - if (!missingAttrs.isEmpty()) { - return LmsSetupTestResult.ofMissingAttributes(LmsType.MOCKUP, missingAttrs); - } - - if (authenticate()) { - return LmsSetupTestResult.ofOkay(LmsType.MOCKUP); - } else { - return LmsSetupTestResult.ofTokenRequestError(LmsType.MOCKUP, "Illegal access"); - } - } - - @Override - public LmsSetupTestResult testCourseRestrictionAPI() { - return LmsSetupTestResult.ofQuizRestrictionAPIError(LmsType.MOCKUP, "unsupported"); - } - - @Override - public Result> getQuizzes(final FilterMap filterMap) { - return this.abstractCourseAccess.protectedQuizzesRequest(filterMap); - } - - private Result> getQuizzes_protected(final FilterMap filterMap) { - return Result.tryCatch(() -> { - if (!authenticate()) { - throw new IllegalArgumentException("Wrong clientId or secret"); - } - - return this.mockups - .stream() - .map(this::getExternalAddressAlias) - .filter(LmsAPIService.quizFilterPredicate(filterMap)) - .collect(Collectors.toList()); - }); - } - - @Override - public Result getQuiz(final String id) { - return this.abstractCourseAccess.protectedQuizRequest(id); - } - - private Result getQuiz_protected(final String id) { - return Result.of(this.mockups - .stream() - .filter(q -> id.equals(q.id)) - .findFirst() - .get()); - } - - @Override - public Result> getQuizzes(final Set ids) { - return this.abstractCourseAccess.protectedQuizzesRequest(ids); - } - - private Result> getQuizzes_protected(final Set ids) { - - return Result.tryCatch(() -> { - if (!authenticate()) { - throw new IllegalArgumentException("Wrong clientId or secret"); - } - - return this.mockups - .stream() - .map(this::getExternalAddressAlias) - .filter(mock -> ids.contains(mock.id)) - .collect(Collectors.toList()); - }); - } - - @Override - public void clearCache() { - - } - - @Override - public Result getCourseChapters(final String courseId) { - return this.abstractCourseAccess.getCourseChapters(courseId); - } - - @Override - public Result getExamineeAccountDetails(final String examineeSessionId) { - return this.abstractCourseAccess.getExamineeAccountDetails(examineeSessionId); - } - - private Result getExamineeAccountDetails_protected(final String examineeSessionId) { - return Result.ofError(new UnsupportedOperationException()); - } - - @Override - public String getExamineeName(final String examineeSessionId) { - return "--" + " (" + examineeSessionId + ")"; - } - - @Override - public Result getSEBClientRestriction(final Exam exam) { - log.info("Apply SEB Client restriction for Exam: {}", exam); - return Result.ofError(new NoSEBRestrictionException()); - } - - @Override - public Result applySEBClientRestriction( - final String externalExamId, - final SEBRestriction sebRestrictionData) { - - log.info("Apply SEB Client restriction: {}", sebRestrictionData); - return Result.of(sebRestrictionData); - } - - @Override - public Result releaseSEBClientRestriction(final Exam exam) { - log.info("Release SEB Client restriction for Exam: {}", exam); - return Result.of(exam); - } - - private QuizData getExternalAddressAlias(final QuizData quizData) { - final String externalAddressAlias = this.webserviceInfo.getLmsExternalAddressAlias("lms.mockup.com"); - if (StringUtils.isNoneBlank(externalAddressAlias)) { - try { - - final String _externalStartURI = - this.webserviceInfo.getHttpScheme() + - "://" + externalAddressAlias + "/api/"; - - return new QuizData( - quizData.id, quizData.institutionId, quizData.lmsSetupId, quizData.lmsType, - quizData.name, quizData.description, quizData.startTime, - quizData.endTime, _externalStartURI, quizData.additionalAttributes); - } catch (final Exception e) { - log.error("Failed to create external address from alias: ", e); - return quizData; - } - } else { - return quizData; - } - } - - private boolean authenticate() { - try { - - final CharSequence plainClientId = this.apiTemplateDataSupplier.getLmsClientCredentials().clientId; - if (plainClientId == null || plainClientId.length() <= 0) { - throw new IllegalAccessException("Wrong client credential"); - } - - return true; - } catch (final Exception e) { - log.info("Authentication failed: ", e); - return false; - } - } - -} +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.mockup; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.client.ClientCredentials; +import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP; +import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; +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.LmsSetup.LmsType; +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.WebserviceInfo; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.CourseAccessAPI; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; + +public class MockCourseAccessAPI implements CourseAccessAPI { + + private final Collection mockups; + private final WebserviceInfo webserviceInfo; + private final APITemplateDataSupplier apiTemplateDataSupplier; + + public MockCourseAccessAPI( + final APITemplateDataSupplier apiTemplateDataSupplier, + final WebserviceInfo webserviceInfo) { + + this.apiTemplateDataSupplier = apiTemplateDataSupplier; + this.webserviceInfo = webserviceInfo; + this.mockups = new ArrayList<>(); + + final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup(); + final Long lmsSetupId = lmsSetup.id; + final Long institutionId = lmsSetup.getInstitutionId(); + final LmsType lmsType = lmsSetup.getLmsType(); + + this.mockups.add(new QuizData( + "quiz1", institutionId, lmsSetupId, lmsType, "Demo Quiz 1 (MOCKUP)", "

Demo Quiz Mockup

", + "2020-01-01T09:00:00Z", null, "http://lms.mockup.com/api/")); + this.mockups.add(new QuizData( + "quiz2", institutionId, lmsSetupId, lmsType, "Demo Quiz 2 (MOCKUP)", "

Demo Quiz Mockup

", + "2020-01-01T09:00:00Z", "2025-01-01T09:00:00Z", "http://lms.mockup.com/api/")); + this.mockups.add(new QuizData( + "quiz3", institutionId, lmsSetupId, lmsType, "Demo Quiz 3 (MOCKUP)", "

Demo Quiz Mockup

", + "2018-07-30T09:00:00Z", "2018-08-01T00:00:00Z", "http://lms.mockup.com/api/")); + this.mockups.add(new QuizData( + "quiz4", institutionId, lmsSetupId, lmsType, "Demo Quiz 4 (MOCKUP)", "

Demo Quiz Mockup

", + "2018-01-01T00:00:00Z", "2025-01-01T00:00:00Z", "http://lms.mockup.com/api/")); + this.mockups.add(new QuizData( + "quiz5", institutionId, lmsSetupId, lmsType, "Demo Quiz 5 (MOCKUP)", "

Demo Quiz Mockup

", + "2018-01-01T09:00:00Z", "2025-01-01T09:00:00Z", "http://lms.mockup.com/api/")); + this.mockups.add(new QuizData( + "quiz6", institutionId, lmsSetupId, lmsType, "Demo Quiz 6 (MOCKUP)", "

Demo Quiz Mockup

", + "2019-01-01T09:00:00Z", "2025-01-01T09:00:00Z", "http://lms.mockup.com/api/")); + this.mockups.add(new QuizData( + "quiz7", institutionId, lmsSetupId, lmsType, "Demo Quiz 7 (MOCKUP)", "

Demo Quiz Mockup

", + "2018-01-01T09:00:00Z", "2025-01-01T09:00:00Z", "http://lms.mockup.com/api/")); + + this.mockups.add(new QuizData( + "quiz10", institutionId, lmsSetupId, lmsType, "Demo Quiz 10 (MOCKUP)", + "Starts in a minute and ends after five minutes", + DateTime.now(DateTimeZone.UTC).plus(Constants.MINUTE_IN_MILLIS) + .toString(Constants.DEFAULT_DATE_TIME_FORMAT), + DateTime.now(DateTimeZone.UTC).plus(6 * Constants.MINUTE_IN_MILLIS) + .toString(Constants.DEFAULT_DATE_TIME_FORMAT), + "http://lms.mockup.com/api/")); + this.mockups.add(new QuizData( + "quiz11", institutionId, lmsSetupId, lmsType, "Demo Quiz 11 (MOCKUP)", + "Starts in a minute and ends never", + DateTime.now(DateTimeZone.UTC).plus(Constants.MINUTE_IN_MILLIS) + .toString(Constants.DEFAULT_DATE_TIME_FORMAT), + null, + "http://lms.mockup.com/api/")); + +// if (webserviceInfo.hasProfile("dev")) { +// for (int i = 12; i < 50; i++) { +// this.mockups.add(new QuizData( +// "quiz10" + i, institutionId, lmsSetupId, lmsType, "Demo Quiz 10 " + i + " (MOCKUP)", +// i + "_Starts in a minute and ends after five minutes", +// DateTime.now(DateTimeZone.UTC).plus(Constants.MINUTE_IN_MILLIS) +// .toString(Constants.DEFAULT_DATE_TIME_FORMAT), +// DateTime.now(DateTimeZone.UTC).plus(6 * Constants.MINUTE_IN_MILLIS) +// .toString(Constants.DEFAULT_DATE_TIME_FORMAT), +// "http://lms.mockup.com/api/")); +// this.mockups.add(new QuizData( +// "quiz11" + i, institutionId, lmsSetupId, lmsType, "Demo Quiz 11 " + i + " (MOCKUP)", +// i + "_Starts in a minute and ends never", +// DateTime.now(DateTimeZone.UTC).plus(Constants.MINUTE_IN_MILLIS) +// .toString(Constants.DEFAULT_DATE_TIME_FORMAT), +// null, +// "http://lms.mockup.com/api/")); +// } +// } + } + + @Override + public LmsSetupTestResult testCourseAccessAPI() { + log.info("Test Lms Binding for Mockup and LmsSetup: {}", this.apiTemplateDataSupplier.getLmsSetup()); + + final List missingAttrs = checkAttributes(); + + if (!missingAttrs.isEmpty()) { + return LmsSetupTestResult.ofMissingAttributes(LmsType.MOCKUP, missingAttrs); + } + + if (authenticate()) { + return LmsSetupTestResult.ofOkay(LmsType.MOCKUP); + } else { + return LmsSetupTestResult.ofTokenRequestError(LmsType.MOCKUP, "Illegal access"); + } + } + + private List checkAttributes() { + final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup(); + final ClientCredentials lmsClientCredentials = this.apiTemplateDataSupplier.getLmsClientCredentials(); + final List missingAttrs = new ArrayList<>(); + if (StringUtils.isBlank(lmsSetup.lmsApiUrl)) { + missingAttrs.add(APIMessage.fieldValidationError( + LMS_SETUP.ATTR_LMS_URL, + "lmsSetup:lmsUrl:notNull")); + } + if (!lmsClientCredentials.hasClientId()) { + missingAttrs.add(APIMessage.fieldValidationError( + LMS_SETUP.ATTR_LMS_CLIENTNAME, + "lmsSetup:lmsClientname:notNull")); + } + if (!lmsClientCredentials.hasSecret()) { + missingAttrs.add(APIMessage.fieldValidationError( + LMS_SETUP.ATTR_LMS_CLIENTSECRET, + "lmsSetup:lmsClientsecret:notNull")); + } + return missingAttrs; + } + + @Override + public Result> getQuizzes(final FilterMap filterMap) { + return Result.tryCatch(() -> { + if (!authenticate()) { + throw new IllegalArgumentException("Wrong clientId or secret"); + } + + return this.mockups + .stream() + .map(this::getExternalAddressAlias) + .filter(LmsAPIService.quizFilterPredicate(filterMap)) + .collect(Collectors.toList()); + }); + } + + @Override + public Result> getQuizzes(final Set ids) { + return Result.tryCatch(() -> { + if (!authenticate()) { + throw new IllegalArgumentException("Wrong clientId or secret"); + } + + return this.mockups + .stream() + .map(this::getExternalAddressAlias) + .filter(mock -> ids.contains(mock.id)) + .collect(Collectors.toList()); + }); + } + + @Override + public Result getQuiz(final String id) { + return Result.of(this.mockups + .stream() + .map(this::getExternalAddressAlias) + .filter(q -> id.equals(q.id)) + .findFirst() + .get()); + } + + @Override + public void clearCourseCache() { + // No cache here + } + + @Override + public Result getExamineeAccountDetails(final String examineeUserId) { + return Result.ofError(new UnsupportedOperationException()); + } + + @Override + public String getExamineeName(final String examineeUserId) { + return "--" + " (" + examineeUserId + ")"; + } + + @Override + public Result getCourseChapters(final String courseId) { + return Result.ofError(new UnsupportedOperationException("Course Chapter feature not supported")); + } + + private boolean authenticate() { + try { + + final CharSequence plainClientId = this.apiTemplateDataSupplier.getLmsClientCredentials().clientId; + if (plainClientId == null || plainClientId.length() <= 0) { + throw new IllegalAccessException("Wrong client credential"); + } + + return true; + } catch (final Exception e) { + log.info("Authentication failed: ", e); + return false; + } + } + + private QuizData getExternalAddressAlias(final QuizData quizData) { + final String externalAddressAlias = this.webserviceInfo.getLmsExternalAddressAlias("lms.mockup.com"); + if (StringUtils.isNoneBlank(externalAddressAlias)) { + try { + + final String _externalStartURI = + this.webserviceInfo.getHttpScheme() + + "://" + externalAddressAlias + "/api/"; + + return new QuizData( + quizData.id, quizData.institutionId, quizData.lmsSetupId, quizData.lmsType, + quizData.name, quizData.description, quizData.startTime, + quizData.endTime, _externalStartURI, quizData.additionalAttributes); + } catch (final Exception e) { + log.error("Failed to create external address from alias: ", e); + return quizData; + } + } else { + return quizData; + } + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockLmsAPITemplateFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockLmsAPITemplateFactory.java index 032e7d6a..911b1ab2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockLmsAPITemplateFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockLmsAPITemplateFactory.java @@ -20,6 +20,7 @@ import ch.ethz.seb.sebserver.webservice.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplateFactory; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsAPITemplateAdapter; @Lazy @Service @@ -47,11 +48,19 @@ public class MockLmsAPITemplateFactory implements LmsAPITemplateFactory { @Override public Result create(final APITemplateDataSupplier apiTemplateDataSupplier) { - return Result.tryCatch(() -> new MockupLmsAPITemplate( + + final MockCourseAccessAPI mockCourseAccessAPI = new MockCourseAccessAPI( + apiTemplateDataSupplier, + this.webserviceInfo); + + final MockSEBRestrictionAPI mockSEBRestrictionAPI = new MockSEBRestrictionAPI(); + + return Result.tryCatch(() -> new LmsAPITemplateAdapter( this.asyncService, this.environment, apiTemplateDataSupplier, - this.webserviceInfo)); + mockCourseAccessAPI, + mockSEBRestrictionAPI)); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockSEBRestrictionAPI.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockSEBRestrictionAPI.java new file mode 100644 index 00000000..5cfe2360 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockSEBRestrictionAPI.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.mockup; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; +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.SEBRestrictionAPI; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.NoSEBRestrictionException; + +public class MockSEBRestrictionAPI implements SEBRestrictionAPI { + + private static final Logger log = LoggerFactory.getLogger(MockSEBRestrictionAPI.class); + + @Override + public LmsSetupTestResult testCourseRestrictionAPI() { + return LmsSetupTestResult.ofQuizRestrictionAPIError(LmsType.MOCKUP, "unsupported"); + } + + @Override + public Result getSEBClientRestriction(final Exam exam) { + log.info("Apply SEB Client restriction for Exam: {}", exam); + return Result.ofError(new NoSEBRestrictionException()); + } + + @Override + public Result applySEBClientRestriction( + final String externalExamId, + final SEBRestriction sebRestrictionData) { + + log.info("Apply SEB Client restriction: {}", sebRestrictionData); + return Result.of(sebRestrictionData); + } + + @Override + public Result releaseSEBClientRestriction(final Exam exam) { + log.info("Release SEB Client restriction for Exam: {}", exam); + return Result.of(exam); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplate.java deleted file mode 100644 index 264d24ec..00000000 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplate.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET) - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle; - -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; -import ch.ethz.seb.sebserver.gbl.model.exam.Exam; -import ch.ethz.seb.sebserver.gbl.model.exam.MoodleSEBRestriction; -import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; -import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; -import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; -import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; -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.dao.FilterMap; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.NoSEBRestrictionException; - -/** The MoodleLmsAPITemplate is separated into two parts: - * - MoodleCourseAccess implements the course access API - * - MoodleCourseRestriction implements the SEB restriction API - * - Both uses the MoodleRestTemplateFactore to create a spring based RestTemplate to access the LMS API - * - * NOTE: Because of the missing integration on Moodle side so far the MoodleCourseAccess - * needs to deal with Moodle's standard API functions that don't allow to filter and page course/quiz data - * in an easy and proper way. Therefore we have to fetch all course and quiz data from Moodle before - * filtering and paging can be applied. Since there are possibly thousands of active courses and quizzes - * this moodle course access implements an synchronous fetch as well as an asynchronous fetch strategy. - * The asynchronous fetch strategy is started within a background task that batches the course and quiz - * requests to Moodle and fill up a shared cache. A SEB Server LMS API request will start the - * background task if needed and return immediately to do not block the request. - * The planed Moodle integration on moodle side also defines an improved course access API. This will - * possibly make this synchronous fetch strategy obsolete in the future. */ -public class MoodleLmsAPITemplate implements LmsAPITemplate { - - private static final Logger log = LoggerFactory.getLogger(MoodleLmsAPITemplate.class); - - private final MoodleCourseAccess moodleCourseAccess; - private final MoodleCourseRestriction moodleCourseRestriction; - - protected MoodleLmsAPITemplate( - final MoodleCourseAccess moodleCourseAccess, - final MoodleCourseRestriction moodleCourseRestriction) { - - this.moodleCourseAccess = moodleCourseAccess; - this.moodleCourseRestriction = moodleCourseRestriction; - } - - @Override - public LmsType getType() { - return LmsType.MOODLE; - } - - @Override - public LmsSetup lmsSetup() { - return this.moodleCourseAccess - .getApiTemplateDataSupplier() - .getLmsSetup(); - } - - @Override - public LmsSetupTestResult testCourseAccessAPI() { - return this.moodleCourseAccess.initAPIAccess(); - } - - @Override - public LmsSetupTestResult testCourseRestrictionAPI() { - throw new NoSEBRestrictionException(); - } - - @Override - public Result> getQuizzes(final FilterMap filterMap) { - return this.moodleCourseAccess - .protectedQuizzesRequest(filterMap) - .map(quizzes -> quizzes.stream() - .filter(LmsAPIService.quizFilterPredicate(filterMap)) - .collect(Collectors.toList())); - } - - @Override - public Result getQuiz(final String id) { - return this.moodleCourseAccess.getQuizFromCache(id); - } - - @Override - public Result> getQuizzes(final Set ids) { - return this.moodleCourseAccess.getQuizzesFromCache(ids); - } - - @Override - public void clearCache() { - this.moodleCourseAccess.clearCache(); - } - - @Override - public Result getCourseChapters(final String courseId) { - return Result.tryCatch(() -> this.moodleCourseAccess - .getCourseChaptersSupplier(courseId) - .get()); - } - - @Override - public Result getExamineeAccountDetails(final String examineeSessionId) { - return this.moodleCourseAccess.getExamineeAccountDetails(examineeSessionId); - } - - @Override - public String getExamineeName(final String examineeSessionId) { - return this.moodleCourseAccess.getExamineeName(examineeSessionId); - } - - @Override - public Result getSEBClientRestriction(final Exam exam) { - if (log.isDebugEnabled()) { - log.debug("Get SEB Client restriction for Exam: {}", exam.externalId); - } - - return this.moodleCourseRestriction - .getSEBRestriction(exam.externalId) - .map(restriction -> SEBRestriction.from(exam.id, restriction)); - } - - @Override - public Result applySEBClientRestriction( - final String externalExamId, - final SEBRestriction sebRestrictionData) { - - if (log.isDebugEnabled()) { - log.debug("Apply SEB Client restriction: {}", sebRestrictionData); - } - - return this.moodleCourseRestriction - .updateSEBRestriction( - externalExamId, - MoodleSEBRestriction.from(sebRestrictionData)) - .map(result -> sebRestrictionData); - } - - @Override - public Result releaseSEBClientRestriction(final Exam exam) { - if (log.isDebugEnabled()) { - log.debug("Release SEB Client restriction for Exam: {}", exam); - } - - return this.moodleCourseRestriction - .deleteSEBRestriction(exam.externalId) - .map(result -> exam); - } - -} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplateFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplateFactory.java index 7659d3a7..6db52389 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplateFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplateFactory.java @@ -27,12 +27,21 @@ import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplateFactory; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsAPITemplateAdapter; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleCourseAccess; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleCourseDataAsyncLoader; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleCourseRestriction; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleRestTemplateFactory; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin.MoodlePluginCheck; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin.MoodlePluginCourseAccess; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin.MoodlePluginCourseRestriction; @Lazy @Service @WebServiceProfile public class MoodleLmsAPITemplateFactory implements LmsAPITemplateFactory { + private final MoodlePluginCheck moodlePluginCheck; private final JSONMapper jsonMapper; private final AsyncService asyncService; private final Environment environment; @@ -42,6 +51,7 @@ public class MoodleLmsAPITemplateFactory implements LmsAPITemplateFactory { private final String[] alternativeTokenRequestPaths; protected MoodleLmsAPITemplateFactory( + final MoodlePluginCheck moodlePluginCheck, final JSONMapper jsonMapper, final AsyncService asyncService, final Environment environment, @@ -50,6 +60,7 @@ public class MoodleLmsAPITemplateFactory implements LmsAPITemplateFactory { final ApplicationContext applicationContext, @Value("${sebserver.webservice.lms.moodle.api.token.request.paths:}") final String alternativeTokenRequestPaths) { + this.moodlePluginCheck = moodlePluginCheck; this.jsonMapper = jsonMapper; this.asyncService = asyncService; this.environment = environment; @@ -72,32 +83,48 @@ public class MoodleLmsAPITemplateFactory implements LmsAPITemplateFactory { return Result.tryCatch(() -> { final LmsSetup lmsSetup = apiTemplateDataSupplier.getLmsSetup(); - final MoodleCourseDataAsyncLoader asyncLoaderPrototype = this.applicationContext .getBean(MoodleCourseDataAsyncLoader.class); asyncLoaderPrototype.init(lmsSetup.getModelId()); - final MoodleRestTemplateFactory moodleRestTemplateFactory = new MoodleRestTemplateFactory( - this.jsonMapper, - apiTemplateDataSupplier, - this.clientCredentialService, - this.clientHttpRequestFactoryService, - this.alternativeTokenRequestPaths); + if (this.moodlePluginCheck.checkPluginAvailable(lmsSetup)) { - final MoodleCourseAccess moodleCourseAccess = new MoodleCourseAccess( - this.jsonMapper, - moodleRestTemplateFactory, - asyncLoaderPrototype, - this.asyncService, - this.environment); + final MoodlePluginCourseAccess moodlePluginCourseAccess = new MoodlePluginCourseAccess(); + final MoodlePluginCourseRestriction moodlePluginCourseRestriction = new MoodlePluginCourseRestriction(); - final MoodleCourseRestriction moodleCourseRestriction = new MoodleCourseRestriction( - this.jsonMapper, - moodleRestTemplateFactory); + return new LmsAPITemplateAdapter( + this.asyncService, + this.environment, + apiTemplateDataSupplier, + moodlePluginCourseAccess, + moodlePluginCourseRestriction); - return new MoodleLmsAPITemplate( - moodleCourseAccess, - moodleCourseRestriction); + } else { + + final MoodleRestTemplateFactory moodleRestTemplateFactory = new MoodleRestTemplateFactory( + this.jsonMapper, + apiTemplateDataSupplier, + this.clientCredentialService, + this.clientHttpRequestFactoryService, + this.alternativeTokenRequestPaths); + + final MoodleCourseAccess moodleCourseAccess = new MoodleCourseAccess( + this.jsonMapper, + moodleRestTemplateFactory, + asyncLoaderPrototype, + this.environment); + + final MoodleCourseRestriction moodleCourseRestriction = new MoodleCourseRestriction( + this.jsonMapper, + moodleRestTemplateFactory); + + return new LmsAPITemplateAdapter( + this.asyncService, + this.environment, + apiTemplateDataSupplier, + moodleCourseAccess, + moodleCourseRestriction); + } }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleCourseAccess.java similarity index 82% rename from src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccess.java rename to src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleCourseAccess.java index 54461dfe..91632190 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccess.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleCourseAccess.java @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle; +package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy; import java.util.ArrayList; import java.util.Collection; @@ -16,7 +16,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; -import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -37,9 +36,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; -import ch.ethz.seb.sebserver.gbl.async.AsyncService; -import ch.ethz.seb.sebserver.gbl.async.CircuitBreaker; -import ch.ethz.seb.sebserver.gbl.async.CircuitBreaker.State; import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; @@ -50,10 +46,11 @@ import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCourseAccess; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleCourseDataAsyncLoader.CourseDataShort; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleCourseDataAsyncLoader.CourseQuizShort; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory.MoodleAPIRestTemplate; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.CourseAccessAPI; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleCourseDataAsyncLoader.CourseDataShort; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleCourseDataAsyncLoader.CourseQuizShort; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleRestTemplateFactory.MoodleAPIRestTemplate; /** Implements the LmsAPITemplate for Open edX LMS Course API access. * @@ -69,7 +66,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestT * background task if needed and return immediately to do not block the request. * The planed Moodle integration on moodle side also defines an improved course access API. This will * possibly make this synchronous fetch strategy obsolete in the future. */ -public class MoodleCourseAccess extends AbstractCourseAccess { +public class MoodleCourseAccess implements CourseAccessAPI { private static final long INITIAL_WAIT_TIME = 3 * Constants.SECOND_IN_MILLIS; @@ -94,37 +91,20 @@ public class MoodleCourseAccess extends AbstractCourseAccess { private final JSONMapper jsonMapper; private final MoodleRestTemplateFactory moodleRestTemplateFactory; private final MoodleCourseDataAsyncLoader moodleCourseDataAsyncLoader; - private final CircuitBreaker> allQuizzesRequest; private final boolean prependShortCourseName; private MoodleAPIRestTemplate restTemplate; - protected MoodleCourseAccess( + public MoodleCourseAccess( final JSONMapper jsonMapper, final MoodleRestTemplateFactory moodleRestTemplateFactory, final MoodleCourseDataAsyncLoader moodleCourseDataAsyncLoader, - final AsyncService asyncService, final Environment environment) { - super(asyncService, environment); this.jsonMapper = jsonMapper; this.moodleCourseDataAsyncLoader = moodleCourseDataAsyncLoader; this.moodleRestTemplateFactory = moodleRestTemplateFactory; - this.allQuizzesRequest = asyncService.createCircuitBreaker( - environment.getProperty( - "sebserver.webservice.circuitbreaker.allQuizzesRequest.attempts", - Integer.class, - 3), - environment.getProperty( - "sebserver.webservice.circuitbreaker.allQuizzesRequest.blockingTime", - Long.class, - Constants.MINUTE_IN_MILLIS), - environment.getProperty( - "sebserver.webservice.circuitbreaker.allQuizzesRequest.timeToRecover", - Long.class, - Constants.MINUTE_IN_MILLIS)); - this.prependShortCourseName = BooleanUtils.toBoolean(environment.getProperty( "sebserver.webservice.lms.moodle.prependShortCourseName", Constants.TRUE_STRING)); @@ -135,67 +115,7 @@ public class MoodleCourseAccess extends AbstractCourseAccess { } @Override - protected Supplier accountDetailsSupplier(final String examineeSessionId) { - return () -> { - try { - final MoodleAPIRestTemplate template = getRestTemplate() - .getOrThrow(); - - final MultiValueMap queryAttributes = new LinkedMultiValueMap<>(); - queryAttributes.add("field", "id"); - queryAttributes.add("values[0]", examineeSessionId); - - final String userDetailsJSON = template.callMoodleAPIFunction( - MOODLE_USER_PROFILE_API_FUNCTION_NAME, - queryAttributes); - - if (checkAccessDeniedError(userDetailsJSON)) { - final LmsSetup lmsSetup = getApiTemplateDataSupplier().getLmsSetup(); - log.error("Get access denied error from Moodle: {} for API call: {}, response: {}", - lmsSetup, - MOODLE_USER_PROFILE_API_FUNCTION_NAME, - Utils.truncateText(userDetailsJSON, 2000)); - throw new RuntimeException("No user details on Moodle API request (access-denied)"); - } - - final MoodleUserDetails[] userDetails = this.jsonMapper. readValue( - userDetailsJSON, - new TypeReference() { - }); - - if (userDetails == null || userDetails.length <= 0) { - throw new RuntimeException("No user details on Moodle API request"); - } - - final Map additionalAttributes = new HashMap<>(); - additionalAttributes.put("firstname", userDetails[0].firstname); - additionalAttributes.put("lastname", userDetails[0].lastname); - additionalAttributes.put("department", userDetails[0].department); - additionalAttributes.put("firstaccess", String.valueOf(userDetails[0].firstaccess)); - additionalAttributes.put("lastaccess", String.valueOf(userDetails[0].lastaccess)); - additionalAttributes.put("auth", userDetails[0].auth); - additionalAttributes.put("suspended", String.valueOf(userDetails[0].suspended)); - additionalAttributes.put("confirmed", String.valueOf(userDetails[0].confirmed)); - additionalAttributes.put("lang", userDetails[0].lang); - additionalAttributes.put("theme", userDetails[0].theme); - additionalAttributes.put("timezone", userDetails[0].timezone); - additionalAttributes.put("description", userDetails[0].description); - additionalAttributes.put("mailformat", String.valueOf(userDetails[0].mailformat)); - additionalAttributes.put("descriptionformat", String.valueOf(userDetails[0].descriptionformat)); - return new ExamineeAccountDetails( - userDetails[0].id, - userDetails[0].fullname, - userDetails[0].username, - userDetails[0].email, - additionalAttributes); - } catch (final Exception e) { - throw new RuntimeException(e); - } - }; - } - - LmsSetupTestResult initAPIAccess() { - + public LmsSetupTestResult testCourseAccessAPI() { final LmsSetupTestResult attributesCheck = this.moodleRestTemplateFactory.test(); if (!attributesCheck.isOk()) { return attributesCheck; @@ -223,7 +143,29 @@ public class MoodleCourseAccess extends AbstractCourseAccess { return LmsSetupTestResult.ofOkay(LmsType.MOODLE); } - public Result getQuizFromCache(final String id) { + @Override + public Result> getQuizzes(final FilterMap filterMap) { + return Result.tryCatch(() -> getRestTemplate() + .map(template -> collectAllQuizzes(template, filterMap)) + .map(quizzes -> quizzes.stream() + .filter(LmsAPIService.quizFilterPredicate(filterMap)) + .collect(Collectors.toList())) + .getOr(Collections.emptyList())); + } + + @Override + public Result> getQuizzes(final Set ids) { + return Result.tryCatch(() -> { + + return getRestTemplate() + .map(template -> getQuizzesForIds(template, ids)) + .onError(error -> log.error("Failed to get courses for: {}", ids, error)) + .getOrElse(() -> Collections.emptyList()); + }); + } + + @Override + public Result getQuiz(final String id) { return Result.tryCatch(() -> { final Map cachedCourseData = this.moodleCourseDataAsyncLoader @@ -255,76 +197,93 @@ public class MoodleCourseAccess extends AbstractCourseAccess { } // get from LMS in protected request - return super.protectedQuizRequest(id).getOrThrow(); - }); - } - - public Result> getQuizzesFromCache(final Set ids) { - return Result.tryCatch(() -> { - final List cached = getCached(); - final List available = (cached != null) - ? cached - : Collections.emptyList(); - - final Map quizMapping = available - .stream() - .collect(Collectors.toMap(q -> q.id, Function.identity())); - - if (!quizMapping.keySet().containsAll(ids)) { - - final Map collect = super.quizzesRequest - .protectedRun(quizzesSupplier(ids)) - .onError(error -> log.error("Failed to get quizzes by ids: ", error)) - .getOrElse(() -> Collections.emptyList()) - .stream() - .collect(Collectors.toMap(qd -> qd.id, Function.identity())); - if (collect != null) { - quizMapping.clear(); - quizMapping.putAll(collect); - } - } - - return quizMapping.values(); - - }); - } - - @Override - protected Supplier quizSupplier(final String id) { - return () -> { final Set ids = Stream.of(id).collect(Collectors.toSet()); return getRestTemplate() .map(template -> getQuizzesForIds(template, ids)) .getOr(Collections.emptyList()) .get(0); - }; + }); } @Override - protected Supplier> quizzesSupplier(final Set ids) { - return () -> getRestTemplate() - .map(template -> getQuizzesForIds(template, ids)) - .getOr(Collections.emptyList()); + public void clearCourseCache() { + // TODO Auto-generated method stub } @Override - protected Supplier> allQuizzesSupplier(final FilterMap filterMap) { - return () -> getRestTemplate() - .map(template -> collectAllQuizzes(template, filterMap)) - .getOr(Collections.emptyList()); + public Result getExamineeAccountDetails(final String examineeSessionId) { + return Result.tryCatch(() -> { + + final MoodleAPIRestTemplate template = getRestTemplate() + .getOrThrow(); + + final MultiValueMap queryAttributes = new LinkedMultiValueMap<>(); + queryAttributes.add("field", "id"); + queryAttributes.add("values[0]", examineeSessionId); + + final String userDetailsJSON = template.callMoodleAPIFunction( + MOODLE_USER_PROFILE_API_FUNCTION_NAME, + queryAttributes); + + if (checkAccessDeniedError(userDetailsJSON)) { + final LmsSetup lmsSetup = getApiTemplateDataSupplier().getLmsSetup(); + log.error("Get access denied error from Moodle: {} for API call: {}, response: {}", + lmsSetup, + MOODLE_USER_PROFILE_API_FUNCTION_NAME, + Utils.truncateText(userDetailsJSON, 2000)); + throw new RuntimeException("No user details on Moodle API request (access-denied)"); + } + + final MoodleUserDetails[] userDetails = this.jsonMapper. readValue( + userDetailsJSON, + new TypeReference() { + }); + + if (userDetails == null || userDetails.length <= 0) { + throw new RuntimeException("No user details on Moodle API request"); + } + + final Map additionalAttributes = new HashMap<>(); + additionalAttributes.put("firstname", userDetails[0].firstname); + additionalAttributes.put("lastname", userDetails[0].lastname); + additionalAttributes.put("department", userDetails[0].department); + additionalAttributes.put("firstaccess", String.valueOf(userDetails[0].firstaccess)); + additionalAttributes.put("lastaccess", String.valueOf(userDetails[0].lastaccess)); + additionalAttributes.put("auth", userDetails[0].auth); + additionalAttributes.put("suspended", String.valueOf(userDetails[0].suspended)); + additionalAttributes.put("confirmed", String.valueOf(userDetails[0].confirmed)); + additionalAttributes.put("lang", userDetails[0].lang); + additionalAttributes.put("theme", userDetails[0].theme); + additionalAttributes.put("timezone", userDetails[0].timezone); + additionalAttributes.put("description", userDetails[0].description); + additionalAttributes.put("mailformat", String.valueOf(userDetails[0].mailformat)); + additionalAttributes.put("descriptionformat", String.valueOf(userDetails[0].descriptionformat)); + return new ExamineeAccountDetails( + userDetails[0].id, + userDetails[0].fullname, + userDetails[0].username, + userDetails[0].email, + additionalAttributes); + }); } @Override - protected Supplier getCourseChaptersSupplier(final String courseId) { - throw new UnsupportedOperationException("not available yet"); + public String getExamineeName(final String examineeUserId) { + return getExamineeAccountDetails(examineeUserId) + .map(ExamineeAccountDetails::getDisplayName) + .onError(error -> log.warn("Failed to request user-name for ID: {}", error.getMessage(), error)) + .getOr(examineeUserId); } @Override - protected FetchStatus getFetchStatus() { - if (this.allQuizzesRequest.getState() != State.CLOSED) { - return FetchStatus.FETCH_ERROR; - } + public Result getCourseChapters(final String courseId) { + return Result.ofError(new UnsupportedOperationException("not available yet")); + } + + @Override + public FetchStatus getFetchStatus() { + if (this.moodleCourseDataAsyncLoader.isRunning()) { return FetchStatus.ASYNC_FETCH_RUNNING; } @@ -395,17 +354,6 @@ public class MoodleCourseAccess extends AbstractCourseAccess { return reduceCoursesToQuizzes(urlPrefix, courseQuizData); } - private List getCached() { - final Collection courseQuizData = - this.moodleCourseDataAsyncLoader.getCachedCourseData().values(); - final LmsSetup lmsSetup = getApiTemplateDataSupplier().getLmsSetup(); - final String urlPrefix = (lmsSetup.lmsApiUrl.endsWith(Constants.URL_PATH_SEPARATOR)) - ? lmsSetup.lmsApiUrl + MOODLE_QUIZ_START_URL_PATH - : lmsSetup.lmsApiUrl + Constants.URL_PATH_SEPARATOR + MOODLE_QUIZ_START_URL_PATH; - - return reduceCoursesToQuizzes(urlPrefix, courseQuizData); - } - private ArrayList reduceCoursesToQuizzes( final String urlPrefix, final Collection courseQuizData) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseDataAsyncLoader.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleCourseDataAsyncLoader.java similarity index 96% rename from src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseDataAsyncLoader.java rename to src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleCourseDataAsyncLoader.java index 290afbc2..35056b6f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseDataAsyncLoader.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleCourseDataAsyncLoader.java @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle; +package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy; import java.io.IOException; import java.util.ArrayList; @@ -47,8 +47,8 @@ import ch.ethz.seb.sebserver.gbl.async.AsyncService; import ch.ethz.seb.sebserver.gbl.async.CircuitBreaker; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Utils; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleCourseAccess.Warning; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory.MoodleAPIRestTemplate; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleCourseAccess.Warning; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleRestTemplateFactory.MoodleAPIRestTemplate; @Lazy @Component diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseRestriction.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleCourseRestriction.java similarity index 88% rename from src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseRestriction.java rename to src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleCourseRestriction.java index c36ef09e..ccff2e87 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseRestriction.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleCourseRestriction.java @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle; +package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy; import java.util.ArrayList; @@ -22,12 +22,15 @@ 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.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.MoodleSEBRestriction; +import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; 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.SEBRestrictionAPI; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.NoSEBRestrictionException; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory.MoodleAPIRestTemplate; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleRestTemplateFactory.MoodleAPIRestTemplate; /** GET: * http://yourmoodle.org/webservice/rest/server.php?wstoken={token}&moodlewsrestformat=json&wsfunction=seb_restriction&courseId=123 @@ -57,7 +60,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestT * Delete all key (and remove restrictions): * POST: * http://yourmoodle.org/webservice/rest/server.php?wstoken={token}&moodlewsrestformat=json&wsfunction=seb_restriction_delete&courseId=123 */ -public class MoodleCourseRestriction { +public class MoodleCourseRestriction implements SEBRestrictionAPI { private static final Logger log = LoggerFactory.getLogger(MoodleCourseRestriction.class); @@ -76,7 +79,7 @@ public class MoodleCourseRestriction { private MoodleAPIRestTemplate restTemplate; - protected MoodleCourseRestriction( + public MoodleCourseRestriction( final JSONMapper jsonMapper, final MoodleRestTemplateFactory moodleRestTemplateFactory) { @@ -84,7 +87,8 @@ public class MoodleCourseRestriction { this.moodleRestTemplateFactory = moodleRestTemplateFactory; } - LmsSetupTestResult initAPIAccess() { + @Override + public LmsSetupTestResult testCourseRestrictionAPI() { // try to call the SEB Restrictions API try { @@ -108,18 +112,35 @@ public class MoodleCourseRestriction { return LmsSetupTestResult.ofOkay(LmsType.MOODLE); } - Result getSEBRestriction( - final String internalId) { - + @Override + public Result getSEBClientRestriction(final Exam exam) { return Result.tryCatch(() -> { return getSEBRestriction( - MoodleCourseAccess.getQuizId(internalId), - MoodleCourseAccess.getShortname(internalId), - MoodleCourseAccess.getIdnumber(internalId)) + MoodleCourseAccess.getQuizId(exam.externalId), + MoodleCourseAccess.getShortname(exam.externalId), + MoodleCourseAccess.getIdnumber(exam.externalId)) + .map(restriction -> SEBRestriction.from(exam.id, restriction)) .getOrThrow(); }); } + @Override + public Result applySEBClientRestriction( + final String externalExamId, + final SEBRestriction sebRestrictionData) { + + return this.updateSEBRestriction( + externalExamId, + MoodleSEBRestriction.from(sebRestrictionData)) + .map(result -> sebRestrictionData); + } + + @Override + public Result releaseSEBClientRestriction(final Exam exam) { + return this.deleteSEBRestriction(exam.externalId) + .map(result -> exam); + } + Result getSEBRestriction( final String quizId, final String shortname, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleRestTemplateFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleRestTemplateFactory.java similarity index 99% rename from src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleRestTemplateFactory.java rename to src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleRestTemplateFactory.java index 39fc9654..5de31e82 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleRestTemplateFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleRestTemplateFactory.java @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle; +package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy; import java.util.ArrayList; import java.util.Arrays; @@ -53,7 +53,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier; -class MoodleRestTemplateFactory { +public class MoodleRestTemplateFactory { private static final Logger log = LoggerFactory.getLogger(MoodleRestTemplateFactory.class); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCheck.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCheck.java new file mode 100644 index 00000000..f0f9955b --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCheck.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin; + +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; +import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; + +@Lazy +@Service +@WebServiceProfile +public class MoodlePluginCheck { + + /** Used to check if the moodle SEB Server plugin is available for a given LMSSetup. + * + * @param lmsSetup The LMS Setup + * @return true if the SEB Server plugin is available */ + public boolean checkPluginAvailable(final LmsSetup lmsSetup) { + // TODO check if the moodle plugin is installed for the specified LMS Setup + return false; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java new file mode 100644 index 00000000..83dc3ba2 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; +import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; +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.dao.FilterMap; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.CourseAccessAPI; + +public class MoodlePluginCourseAccess implements CourseAccessAPI { + + @Override + public LmsSetupTestResult testCourseAccessAPI() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Result> getQuizzes(final FilterMap filterMap) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Result> getQuizzes(final Set ids) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Result getQuiz(final String id) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void clearCourseCache() { + // TODO Auto-generated method stub + + } + + @Override + public Result getExamineeAccountDetails(final String examineeUserId) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getExamineeName(final String examineeUserId) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Result getCourseChapters(final String courseId) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseRestriction.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseRestriction.java new file mode 100644 index 00000000..d2eeb5e3 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseRestriction.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin; + +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; +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.SEBRestrictionAPI; + +public class MoodlePluginCourseRestriction implements SEBRestrictionAPI { + + @Override + public LmsSetupTestResult testCourseRestrictionAPI() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Result getSEBClientRestriction(final Exam exam) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Result applySEBClientRestriction(final String externalExamId, + final SEBRestriction sebRestrictionData) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Result releaseSEBClientRestriction(final Exam exam) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java index 3a84b410..6b27ca98 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java @@ -14,8 +14,8 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; @@ -24,7 +24,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.CacheManager; import org.springframework.core.ParameterizedTypeReference; -import org.springframework.core.env.Environment; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -35,7 +34,6 @@ import org.springframework.web.client.RestTemplate; import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService; import ch.ethz.seb.sebserver.gbl.api.APIMessage; -import ch.ethz.seb.sebserver.gbl.async.AsyncService; import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService; import ch.ethz.seb.sebserver.gbl.client.ClientCredentials; import ch.ethz.seb.sebserver.gbl.client.ProxyData; @@ -75,11 +73,9 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm final ClientHttpRequestFactoryService clientHttpRequestFactoryService, final ClientCredentialService clientCredentialService, final APITemplateDataSupplier apiTemplateDataSupplier, - final AsyncService asyncService, - final Environment environment, final CacheManager cacheManager) { - super(asyncService, environment, cacheManager); + super(cacheManager); this.clientHttpRequestFactoryService = clientHttpRequestFactoryService; this.clientCredentialService = clientCredentialService; @@ -166,7 +162,7 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm @Override public Result> getQuizzes(final FilterMap filterMap) { return this - .protectedQuizzesRequest(filterMap) + .allQuizzesRequest(filterMap) .map(quizzes -> quizzes.stream() .filter(LmsAPIService.quizFilterPredicate(filterMap)) .collect(Collectors.toList())); @@ -187,7 +183,7 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm }); if (!leftIds.isEmpty()) { - result.addAll(super.protectedQuizzesRequest(leftIds).getOrThrow()); + result.addAll(quizzesRequest(leftIds).getOrThrow()); } return result; @@ -201,18 +197,35 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm return Result.of(fromCache); } - return super.protectedQuizRequest(id); + return quizRequest(id); } @Override - protected Supplier> allQuizzesSupplier(final FilterMap filterMap) { - return () -> { + public Result getExamineeAccountDetails(final String examineeUserId) { + return getRestTemplate().map(t -> this.getExamineeById(t, examineeUserId)); + } + + @Override + public String getExamineeName(final String examineeUserId) { + return getExamineeAccountDetails(examineeUserId) + .map(ExamineeAccountDetails::getDisplayName) + .onError(error -> log.warn("Failed to request user-name for ID: {}", error.getMessage(), error)) + .getOr(examineeUserId); + } + + @Override + public Result getCourseChapters(final String courseId) { + return Result.ofError(new UnsupportedOperationException("No Course Chapter available for OpenOLAT LMS")); + } + + protected Result> allQuizzesRequest(final FilterMap filterMap) { + return Result.tryCatch(() -> { final List res = getRestTemplate() .map(t -> this.collectAllQuizzes(t, filterMap)) .getOrThrow(); super.putToCache(res); return res; - }; + }); } private String examUrl(final long olatRepositoryId) { @@ -254,16 +267,15 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm .collect(Collectors.toList()); } - @Override - protected Supplier> quizzesSupplier(final Set ids) { - return () -> ids.stream().map(id -> quizSupplier(id).get()).collect(Collectors.toList()); + protected Result> quizzesRequest(final Set ids) { + return Result.tryCatch(() -> ids.stream() + .map(id -> quizRequest(id).getOr(null)) + .filter(Objects::nonNull) + .collect(Collectors.toList())); } - @Override - protected Supplier quizSupplier(final String id) { - return () -> getRestTemplate() - .map(t -> this.quizById(t, id)) - .getOrThrow(); + protected Result quizRequest(final String id) { + return getRestTemplate().map(t -> this.quizById(t, id)); } private QuizData quizById(final OlatLmsRestTemplate restTemplate, final String id) { @@ -295,20 +307,6 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm attrs); } - @Override - protected Supplier accountDetailsSupplier(final String id) { - return () -> getRestTemplate() - .map(t -> this.getExamineeById(t, id)) - .getOrThrow(); - } - - @Override - protected Supplier getCourseChaptersSupplier(final String courseId) { - return () -> { - throw new UnsupportedOperationException("No Course Chapter available for OpenOLAT LMS"); - }; - } - private SEBRestriction getRestrictionForAssignmentId(final RestTemplate restTemplate, final String id) { final String url = String.format("/restapi/assessment_modes/%s/seb_restriction", id); final RestrictionData r = this.apiGet(restTemplate, url, RestrictionData.class); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplateFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplateFactory.java index cb4b71a9..3330737b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplateFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplateFactory.java @@ -22,6 +22,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplateFactory; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsAPITemplateAdapter; @Lazy @Service @@ -63,13 +64,17 @@ public class OlatLmsAPITemplateFactory implements LmsAPITemplateFactory { @Override public Result create(final APITemplateDataSupplier apiTemplateDataSupplier) { return Result.tryCatch(() -> { - return new OlatLmsAPITemplate( + final OlatLmsAPITemplate olatLmsAPITemplate = new OlatLmsAPITemplate( this.clientHttpRequestFactoryService, this.clientCredentialService, apiTemplateDataSupplier, + this.cacheManager); + return new LmsAPITemplateAdapter( this.asyncService, this.environment, - this.cacheManager); + apiTemplateDataSupplier, + olatLmsAPITemplate, + olatLmsAPITemplate); }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsRestTemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsRestTemplate.java index bc026a1f..533a3acc 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsRestTemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsRestTemplate.java @@ -39,29 +39,67 @@ public class OlatLmsRestTemplate extends RestTemplate { // Add X-OLAT-TOKEN request header to every request done using this RestTemplate this.getInterceptors().add(new ClientHttpRequestInterceptor() { @Override - public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, + public synchronized ClientHttpResponse intercept( + final HttpRequest request, + final byte[] body, final ClientHttpRequestExecution execution) throws IOException { - // if there's no token, authenticate first - if (OlatLmsRestTemplate.this.token == null) { - authenticate(); - } - // when authenticating, just do a normal call - else if (OlatLmsRestTemplate.this.token.equals("authenticating")) { - return execution.execute(request, body); - } - // otherwise, add the X-OLAT-TOKEN - request.getHeaders().set("accept", "application/json"); - request.getHeaders().set("X-OLAT-TOKEN", OlatLmsRestTemplate.this.token); - ClientHttpResponse response = execution.execute(request, body); - log.debug("OLAT [regular API call] {} Headers: {}", response.getStatusCode(), response.getHeaders()); - // If we get a 401, re-authenticate and try once more - if (response.getStatusCode() == HttpStatus.UNAUTHORIZED) { - authenticate(); + + try { + + // if there's no token, authenticate first + if (OlatLmsRestTemplate.this.token == null) { + authenticate(); + } + // when authenticating, just do a normal call + else if (OlatLmsRestTemplate.this.token.equals("authenticating")) { + + if (log.isDebugEnabled()) { + log.debug("OLAT [authentication call]: URL {}", request.getURI()); + } + + return execution.execute(request, body); + } + // otherwise, add the X-OLAT-TOKEN + request.getHeaders().set("accept", "application/json"); request.getHeaders().set("X-OLAT-TOKEN", OlatLmsRestTemplate.this.token); - response = execution.execute(request, body); - log.debug("OLAT [retry API call] {} Headers: {}", response.getStatusCode(), response.getHeaders()); + + if (log.isDebugEnabled()) { + log.debug("OLAT [regular API call]: URL {}", request.getURI()); + } + + ClientHttpResponse response = execution.execute(request, body); + + if (log.isDebugEnabled()) { + log.debug("OLAT [regular API call response] {} Headers: {}", + response.getStatusCode(), + response.getHeaders()); + } + + // If we get a 401, re-authenticate and try once more + if (response.getStatusCode() == HttpStatus.UNAUTHORIZED) { + + authenticate(); + request.getHeaders().set("X-OLAT-TOKEN", OlatLmsRestTemplate.this.token); + + if (log.isDebugEnabled()) { + log.debug("OLAT [retry API call]: URL {}", request.getURI()); + } + + response = execution.execute(request, body); + + if (log.isDebugEnabled()) { + log.debug("OLAT [retry API call response] {} Headers: {}", + response.getStatusCode(), + response.getHeaders()); + } + } + return response; + + } catch (final Exception e) { + // TODO find a way to better deal with Olat temporary unavailability + log.error("Unexpected error: ", e); + throw e; } - return response; } }); } @@ -75,11 +113,15 @@ public class OlatLmsRestTemplate extends RestTemplate { credentials.put("password", this.details.getClientSecret()); final HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.set("content-type", "application/json"); - final HttpEntity> requestEntity = new HttpEntity<>(credentials, httpHeaders); + final HttpEntity> requestEntity = new HttpEntity<>(credentials, httpHeaders); try { final ResponseEntity response = this.postForEntity(authUrl, requestEntity, String.class); final HttpHeaders responseHeaders = response.getHeaders(); - log.debug("OLAT [authenticate] {} Headers: {}", response.getStatusCode(), responseHeaders); + + if (log.isDebugEnabled()) { + log.debug("OLAT [authenticated] {} Headers: {}", response.getStatusCode(), responseHeaders); + } + this.token = responseHeaders.getFirst("X-OLAT-TOKEN"); } catch (final Exception e) { this.token = null; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ExamConfigService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ExamConfigService.java index 41736016..6155e2cd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ExamConfigService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ExamConfigService.java @@ -14,6 +14,7 @@ import java.util.Collection; import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException; 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.ConfigurationTableValues; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -142,4 +143,19 @@ public interface ExamConfigService { * ConfigurationNode */ Result hasUnpublishedChanged(Long institutionId, Long configurationNodeId); + /** Used to reset the settings of a given configuration to the settings of its origin template. + * If the given configuration has no origin template, an error will be reported. + * + * NOTE: This do not publish the changes (applied template settings). + * + * @param configurationNode The ConfigurationNode + * @return Result refer to the configuration with reseted settings or to an error when happened */ + Result resetToTemplateSettings(ConfigurationNode configurationNode); + + /** Checks if given configuration is ready to save. + * + * @param configurationNode the ConfigurationNode instance + * @return Result refer to the given ConfigurationNode or to an error if the check has failed */ + Result checkSaveConsistency(ConfigurationNode configurationNode); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SEBConfigEncryptionContext.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SEBConfigEncryptionContext.java index 729070f3..9e711d12 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SEBConfigEncryptionContext.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SEBConfigEncryptionContext.java @@ -36,4 +36,6 @@ public interface SEBConfigEncryptionContext { * @throws UnsupportedOperationException if not supported */ Certificate getCertificate(); + String getCertificateAlias(); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/AbstractCertificateCryptor.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/AbstractCertificateCryptor.java index 84eb9115..b07dcd62 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/AbstractCertificateCryptor.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/AbstractCertificateCryptor.java @@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; import java.io.IOException; import java.io.InputStream; +import java.security.PrivateKey; import java.security.cert.Certificate; import java.util.Arrays; import java.util.Enumeration; @@ -21,7 +22,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Certificates; +import ch.ethz.seb.sebserver.gbl.util.Cryptor; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.CertificateService; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncryptionContext; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncryptionService; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.SEBConfigEncryptionServiceImpl.EncryptionContext; public abstract class AbstractCertificateCryptor { @@ -32,25 +37,38 @@ public abstract class AbstractCertificateCryptor { protected static final int KEY_LENGTH_SIZE = 4; protected final CertificateService certificateService; + protected final Cryptor cryptor; + + public AbstractCertificateCryptor( + final CertificateService certificateService, + final Cryptor cryptor) { - public AbstractCertificateCryptor(final CertificateService certificateService) { this.certificateService = certificateService; + this.cryptor = cryptor; } - protected Certificate getCertificateByPublicKeyHash(final Long institutionId, final byte[] publicKeyHash) { + protected SEBConfigEncryptionContext getCertificateByPublicKeyHash( + final SEBConfigEncryptionContext sebConfigEncryptionContext, + final byte[] publicKeyHash) { + try { final Certificates certs = this.certificateService - .getCertificates(institutionId) + .getCertificates(sebConfigEncryptionContext.institutionId()) .getOrThrow(); @SuppressWarnings("unchecked") final Enumeration engineAliases = certs.keyStore.engineAliases(); while (engineAliases.hasMoreElements()) { - final Certificate certificate = certs.keyStore.engineGetCertificate(engineAliases.nextElement()); + final String alias = engineAliases.nextElement(); + final Certificate certificate = certs.keyStore.engineGetCertificate(alias); final byte[] otherPublicKeyHash = generatePublicKeyHash(certificate); if (Arrays.equals(otherPublicKeyHash, publicKeyHash)) { - return certificate; + return EncryptionContext.contextOf( + sebConfigEncryptionContext.institutionId(), + getStrategy(), + certificate, + alias); } } @@ -62,6 +80,8 @@ public abstract class AbstractCertificateCryptor { } } + protected abstract SEBConfigEncryptionService.Strategy getStrategy(); + protected byte[] generatePublicKeyHash(final Certificate cert) { try { @@ -85,21 +105,33 @@ public abstract class AbstractCertificateCryptor { final String algorithm = cert.getPublicKey().getAlgorithm(); final Cipher encryptCipher = Cipher.getInstance(algorithm); - encryptCipher.init(Cipher.ENCRYPT_MODE, cert.getPublicKey()); + encryptCipher.init(Cipher.ENCRYPT_MODE, cert); return encryptCipher.doFinal(data, 0, length); } - protected byte[] decryptWithCert(final Certificate cert, final byte[] encryptedData) throws Exception { - return decryptWithCert(cert, encryptedData, encryptedData.length); + protected byte[] decryptWithCert( + final SEBConfigEncryptionContext sebConfigEncryptionContext, + final byte[] encryptedData) throws Exception { + + return decryptWithCert(sebConfigEncryptionContext, encryptedData, encryptedData.length); } protected byte[] decryptWithCert( - final Certificate cert, - final byte[] encryptedData, final int length) throws Exception { + final SEBConfigEncryptionContext sebConfigEncryptionContext, + final byte[] encryptedData, + final int length) throws Exception { - final String algorithm = cert.getPublicKey().getAlgorithm(); + final Certificate certificate = sebConfigEncryptionContext.getCertificate(); + final String certificateAlias = sebConfigEncryptionContext.getCertificateAlias(); + final String algorithm = certificate.getPublicKey().getAlgorithm(); final Cipher encryptCipher = Cipher.getInstance(algorithm); - encryptCipher.init(Cipher.DECRYPT_MODE, cert.getPublicKey()); + final Certificates certificates = this.certificateService + .getCertificates(sebConfigEncryptionContext.institutionId()) + .getOrThrow(); + final PrivateKey privateKey = this.cryptor + .getPrivateKey(certificates.keyStore, certificateAlias) + .getOrThrow(); + encryptCipher.init(Cipher.DECRYPT_MODE, privateKey); return encryptCipher.doFinal(encryptedData, 0, length); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/CertificateAsymetricKeyCryptor.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/CertificateAsymetricKeyCryptor.java index 159a03fa..835a8590 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/CertificateAsymetricKeyCryptor.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/CertificateAsymetricKeyCryptor.java @@ -20,6 +20,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Cryptor; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.CertificateService; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigCryptor; @@ -37,8 +38,11 @@ public class CertificateAsymetricKeyCryptor extends AbstractCertificateCryptor i private static final Set STRATEGIES = Utils.immutableSetOf( Strategy.PUBLIC_KEY_HASH); - public CertificateAsymetricKeyCryptor(final CertificateService certificateService) { - super(certificateService); + public CertificateAsymetricKeyCryptor( + final CertificateService certificateService, + final Cryptor cryptor) { + + super(certificateService, cryptor); } @Override @@ -94,9 +98,10 @@ public class CertificateAsymetricKeyCryptor extends AbstractCertificateCryptor i try { final byte[] publicKeyHash = parsePublicKeyHash(input); - final Certificate certificate = getCertificateByPublicKeyHash( - context.institutionId(), + final SEBConfigEncryptionContext sebConfigEncryptionContext = getCertificateByPublicKeyHash( + context, publicKeyHash); + final Certificate certificate = sebConfigEncryptionContext.getCertificate(); if (certificate == null) { throw new RuntimeException("No matching certificate found to decrypt the configuration"); @@ -106,7 +111,7 @@ public class CertificateAsymetricKeyCryptor extends AbstractCertificateCryptor i int readBytes = input.read(buffer, 0, buffer.length); while (readBytes > 0) { - final byte[] encryptedBlock = decryptWithCert(certificate, buffer, readBytes); + final byte[] encryptedBlock = decryptWithCert(sebConfigEncryptionContext, buffer, readBytes); output.write(encryptedBlock, 0, encryptedBlock.length); readBytes = input.read(buffer, 0, buffer.length); } @@ -127,4 +132,9 @@ public class CertificateAsymetricKeyCryptor extends AbstractCertificateCryptor i } } + @Override + protected Strategy getStrategy() { + return Strategy.PUBLIC_KEY_HASH; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/CertificateSymetricKeyCryptor.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/CertificateSymetricKeyCryptor.java index 19362bea..b10c8cdf 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/CertificateSymetricKeyCryptor.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/CertificateSymetricKeyCryptor.java @@ -29,6 +29,7 @@ import org.springframework.stereotype.Component; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Cryptor; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.CertificateService; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigCryptor; @@ -60,9 +61,10 @@ public class CertificateSymetricKeyCryptor extends AbstractCertificateCryptor im public CertificateSymetricKeyCryptor( final PasswordEncryptor passwordEncryptor, final PasswordDecryptor passwordDecryptor, - final CertificateService certificateService) { + final CertificateService certificateService, + final Cryptor cryptor) { - super(certificateService); + super(certificateService, cryptor); this.passwordEncryptor = passwordEncryptor; this.passwordDecryptor = passwordDecryptor; } @@ -120,16 +122,17 @@ public class CertificateSymetricKeyCryptor extends AbstractCertificateCryptor im try { final byte[] publicKeyHash = parsePublicKeyHash(input); - final Certificate certificate = getCertificateByPublicKeyHash( - context.institutionId(), + final SEBConfigEncryptionContext sebConfigEncryptionContext = getCertificateByPublicKeyHash( + context, publicKeyHash); + final Certificate certificate = sebConfigEncryptionContext.getCertificate(); if (certificate == null) { throw new RuntimeException("No matching certificate found to decrypt the configuration"); } final byte[] encryptedKey = getEncryptedKey(input); - final byte[] symetricKey = decryptWithCert(certificate, encryptedKey); + final byte[] symetricKey = decryptWithCert(sebConfigEncryptionContext, encryptedKey); final CharSequence symetricKeyBase64 = Base64.getEncoder().encodeToString(symetricKey); this.passwordDecryptor.decrypt(output, input, symetricKeyBase64); @@ -191,4 +194,9 @@ public class CertificateSymetricKeyCryptor extends AbstractCertificateCryptor im return byteArray; } + @Override + protected Strategy getStrategy() { + return Strategy.PUBLIC_KEY_HASH_SYMMETRIC_KEY; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ClientConfigServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ClientConfigServiceImpl.java index 7f065a9c..06f25b7c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ClientConfigServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ClientConfigServiceImpl.java @@ -169,6 +169,7 @@ public class ClientConfigServiceImpl implements ClientConfigService { private final WebserviceInfo webserviceInfo; private final CertificateDAO certificateDAO; private final long defaultPingInterval; + private final int examAPITokenValiditySeconds; protected ClientConfigServiceImpl( final SEBClientConfigDAO sebClientConfigDAO, @@ -178,7 +179,8 @@ public class ClientConfigServiceImpl implements ClientConfigService { final WebserviceInfo webserviceInfo, final CertificateDAO certificateDAO, @Qualifier(WebSecurityConfig.CLIENT_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder clientPasswordEncoder, - @Value("${sebserver.webservice.api.exam.defaultPingInterval:1000}") final long defaultPingInterval) { + @Value("${sebserver.webservice.api.exam.defaultPingInterval:1000}") final long defaultPingInterval, + @Value("${sebserver.webservice.api.exam.accessTokenValiditySeconds:43200}") final int examAPITokenValiditySeconds) { this.sebClientConfigDAO = sebClientConfigDAO; this.clientCredentialService = clientCredentialService; @@ -188,6 +190,7 @@ public class ClientConfigServiceImpl implements ClientConfigService { this.webserviceInfo = webserviceInfo; this.certificateDAO = certificateDAO; this.defaultPingInterval = defaultPingInterval; + this.examAPITokenValiditySeconds = examAPITokenValiditySeconds; } @Override @@ -210,7 +213,8 @@ public class ClientConfigServiceImpl implements ClientConfigService { baseClientDetails.setScope(Collections.emptySet()); baseClientDetails.setClientSecret(Utils.toString(pwd)); - baseClientDetails.setAccessTokenValiditySeconds(-1); // not expiring + baseClientDetails.setAccessTokenValiditySeconds(this.examAPITokenValiditySeconds); + baseClientDetails.setRefreshTokenValiditySeconds(-1); // not used, not expiring if (log.isDebugEnabled()) { log.debug("Created new BaseClientDetails for id: {}", clientName); @@ -293,9 +297,9 @@ public class ClientConfigServiceImpl implements ClientConfigService { private SEBConfigEncryptionContext buildCertificateEncryptionContext(final SEBClientConfig config) { - final Certificate certificate = this.certificateDAO.getCertificate( - config.institutionId, - String.valueOf(config.getEncryptCertificateAlias())) + final String alias = String.valueOf(config.getEncryptCertificateAlias()); + final Certificate certificate = this.certificateDAO + .getCertificate(config.institutionId, alias) .getOrThrow(); return EncryptionContext.contextOf( @@ -303,7 +307,8 @@ public class ClientConfigServiceImpl implements ClientConfigService { (config.encryptCertificateAsym) ? SEBConfigEncryptionService.Strategy.PUBLIC_KEY_HASH : SEBConfigEncryptionService.Strategy.PUBLIC_KEY_HASH_SYMMETRIC_KEY, - certificate); + certificate, + alias); } private SEBConfigEncryptionContext buildPasswordEncryptionContext(final SEBClientConfig config) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigIO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigIO.java index 9e77af50..0adc714e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigIO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigIO.java @@ -15,6 +15,7 @@ import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.SequenceInputStream; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -50,6 +51,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.AttributeValueConverter; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.AttributeValueConverterService; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationFormat; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncryptionService.Strategy; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ZipService; @Lazy @@ -243,6 +245,10 @@ public class ExamConfigIO { throw new IllegalArgumentException("Failed to verify Zip type from input stream. Header size mismatch."); } + if (Arrays.equals(Strategy.PLAIN_TEXT.header, zipHeader)) { + return unzip(input); + } + final boolean isZipped = Byte.toUnsignedInt(zipHeader[0]) == Constants.GZIP_ID1 && Byte.toUnsignedInt(zipHeader[1]) == Constants.GZIP_ID2 && Byte.toUnsignedInt(zipHeader[2]) == Constants.GZIP_CM; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigServiceImpl.java index b0ebf199..2894bc47 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigServiceImpl.java @@ -14,6 +14,7 @@ import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.concurrent.Future; import java.util.stream.Collectors; @@ -33,12 +34,15 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException; import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationFormat; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator; @@ -47,6 +51,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncrypti import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncryptionService.Strategy; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ZipService; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.SEBConfigEncryptionServiceImpl.EncryptionContext; +import ch.ethz.seb.sebserver.webservice.weblayer.api.APIConstraintViolationException; @Lazy @Service @@ -56,6 +61,7 @@ public class ExamConfigServiceImpl implements ExamConfigService { private static final Logger log = LoggerFactory.getLogger(ExamConfigServiceImpl.class); private final ExamConfigIO examConfigIO; + private final ConfigurationNodeDAO configurationNodeDAO; private final ConfigurationAttributeDAO configurationAttributeDAO; private final ExamConfigurationMapDAO examConfigurationMapDAO; private final Collection validators; @@ -66,6 +72,7 @@ public class ExamConfigServiceImpl implements ExamConfigService { protected ExamConfigServiceImpl( final ExamConfigIO examConfigIO, + final ConfigurationNodeDAO configurationNodeDAO, final ConfigurationAttributeDAO configurationAttributeDAO, final ExamConfigurationMapDAO examConfigurationMapDAO, final Collection validators, @@ -75,6 +82,7 @@ public class ExamConfigServiceImpl implements ExamConfigService { final ConfigurationDAO configurationDAO) { this.examConfigIO = examConfigIO; + this.configurationNodeDAO = configurationNodeDAO; this.configurationAttributeDAO = configurationAttributeDAO; this.examConfigurationMapDAO = examConfigurationMapDAO; this.validators = validators; @@ -128,6 +136,7 @@ public class ExamConfigServiceImpl implements ExamConfigService { } } + @Override public Result getFollowupConfigurationId(final Long examConfigNodeId) { return this.configurationDAO.getFollowupConfigurationId(examConfigNodeId); } @@ -397,6 +406,82 @@ public class ExamConfigServiceImpl implements ExamConfigService { }); } + @Override + public Result checkSaveConsistency(final ConfigurationNode configurationNode) { + return Result.tryCatch(() -> { + + // check type compatibility + final ConfigurationNode existingNode = this.configurationNodeDAO + .byPK(configurationNode.id) + .getOrThrow(); + + if (existingNode.type != configurationNode.type) { + throw new APIConstraintViolationException( + "The Type of ConfigurationNode cannot change after creation"); + } + + // if configuration is in use, "Ready to Use" is not possible + if (configurationNode.status == ConfigurationStatus.READY_TO_USE) { + if (!this.examConfigurationMapDAO + .getExamIdsForConfigNodeId(configurationNode.id) + .getOr(Collections.emptyList()) + .isEmpty()) { + throw new APIMessageException( + APIMessage.ErrorMessage.INTEGRITY_VALIDATION + .of("Exam configuration has references to at least one exam.")); + } + } + + // if changing to archived check possibility + if (configurationNode.status == ConfigurationStatus.ARCHIVED) { + if (existingNode.status != ConfigurationStatus.ARCHIVED) { + // check if this is possible (no upcoming or running exams involved) + if (!this.examConfigurationMapDAO.checkNoActiveExamReferences(configurationNode.id).getOr(false)) { + throw new APIMessageException( + APIMessage.ErrorMessage.INTEGRITY_VALIDATION + .of("Exam configuration has references to at least one upcoming or running exam.")); + } + } + } + + // if changing to "In Use" check config is mapped for at least one exam + if (configurationNode.status == ConfigurationStatus.IN_USE && + existingNode.status != ConfigurationStatus.IN_USE) { + + if (this.examConfigurationMapDAO + .getExamIdsForConfigNodeId(configurationNode.id) + .getOr(Collections.emptyList()) + .isEmpty()) { + throw new APIMessageException( + APIMessage.ErrorMessage.INTEGRITY_VALIDATION + .of("Exam configuration has no reference to any exam.")); + } + } + + return configurationNode; + + }); + } + + @Override + public Result resetToTemplateSettings(final ConfigurationNode configurationNode) { + return Result.tryCatch(() -> { + if (configurationNode.templateId == null) { + throw new IllegalAccessException( + "Configuration with name: " + configurationNode.name + " has no template!"); + } + if (configurationNode.status == ConfigurationStatus.IN_USE) { + throw new IllegalStateException("Configuration with name: " + configurationNode.name + " is in use!"); + } + + this.configurationDAO + .restoreToDefaultValues(configurationNode.id) + .getOrThrow(); + + return configurationNode; + }); + } + private void exportPlainOnly( final ConfigurationFormat exportFormat, final OutputStream out, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigTemplateServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigTemplateServiceImpl.java index 8aa340b4..a33d2581 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigTemplateServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ExamConfigTemplateServiceImpl.java @@ -25,6 +25,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.PageSortOrder; import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; @@ -39,6 +40,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeD import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationValueDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.NoResourceFoundException; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.OrientationDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ViewDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ExamConfigTemplateService; @@ -172,7 +174,13 @@ public class ExamConfigTemplateServiceImpl implements ExamConfigTemplateService return Result.tryCatch(() -> { final Orientation orientation = getOrientation(templateId, attributeId); - this.orientationDAO.delete(new HashSet<>(Arrays.asList(orientation.getEntityKey()))) + if (orientation == null) { + throw new NoResourceFoundException(EntityType.ORIENTATION, + "No default view found for attribute: " + attributeId); + } + + this.orientationDAO + .delete(new HashSet<>(Arrays.asList(orientation.getEntityKey()))) .getOrThrow(); final TemplateAttribute attribute = getAttribute(institutionId, templateId, attributeId) @@ -198,8 +206,14 @@ public class ExamConfigTemplateServiceImpl implements ExamConfigTemplateService final Orientation orientation = getOrientation(templateId, attributeId); final Orientation devOrientation = getOrientation(ConfigurationNode.DEFAULT_TEMPLATE_ID, attributeId); + if (devOrientation == null) { + throw new NoResourceFoundException(EntityType.ORIENTATION, + "No default view found for attribute: " + attributeId); + } + if (orientation != null) { - this.orientationDAO.delete(new HashSet<>(Arrays.asList(orientation.getEntityKey()))) + this.orientationDAO + .delete(new HashSet<>(Arrays.asList(orientation.getEntityKey()))) .getOrThrow(); } @@ -246,7 +260,7 @@ public class ExamConfigTemplateServiceImpl implements ExamConfigTemplateService return this.orientationDAO.allMatching(filterMap) .get(error -> { - log.warn("Unexpecrted error while get Orientation: ", error); + log.warn("Unexpected error while get Orientation: ", error); return Collections.emptyList(); }) .stream() diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SEBConfigEncryptionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SEBConfigEncryptionServiceImpl.java index a8b916a9..ce6156fd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SEBConfigEncryptionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SEBConfigEncryptionServiceImpl.java @@ -194,17 +194,20 @@ public final class SEBConfigEncryptionServiceImpl implements SEBConfigEncryption public final Strategy strategy; public final CharSequence password; public final Certificate certificate; + public final String certificateAlias; private EncryptionContext( final Long institutionId, final Strategy strategy, final CharSequence password, - final Certificate certificate) { + final Certificate certificate, + final String certificateAlias) { this.institutionId = institutionId; this.strategy = strategy; this.password = password; this.certificate = certificate; + this.certificateAlias = certificateAlias; } @Override @@ -227,22 +230,28 @@ public final class SEBConfigEncryptionServiceImpl implements SEBConfigEncryption return this.certificate; } + @Override + public String getCertificateAlias() { + return this.certificateAlias; + } + static SEBConfigEncryptionContext contextOf( final Long institutionId, final Strategy strategy, final CharSequence password) { checkPasswordBased(strategy); - return new EncryptionContext(institutionId, strategy, password, null); + return new EncryptionContext(institutionId, strategy, password, null, null); } static SEBConfigEncryptionContext contextOf( final Long institutionId, final Strategy strategy, - final Certificate certificate) { + final Certificate certificate, + final String certificateAlias) { checkCertificateBased(strategy); - return new EncryptionContext(institutionId, strategy, null, certificate); + return new EncryptionContext(institutionId, strategy, null, certificate, certificateAlias); } static void checkPasswordBased(final Strategy strategy) { @@ -259,14 +268,14 @@ public final class SEBConfigEncryptionServiceImpl implements SEBConfigEncryption public static SEBConfigEncryptionContext contextOfPlainText(final Long institutionId) { - return new EncryptionContext(institutionId, Strategy.PLAIN_TEXT, null, null); + return new EncryptionContext(institutionId, Strategy.PLAIN_TEXT, null, null, null); } public static SEBConfigEncryptionContext contextOf( final Long institutionId, final CharSequence password) { - return new EncryptionContext(institutionId, null, password, null); + return new EncryptionContext(institutionId, null, password, null, null); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamFinishedEvent.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamFinishedEvent.java new file mode 100644 index 00000000..72ddaf49 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamFinishedEvent.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.session; + +import org.springframework.context.ApplicationEvent; + +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; + +/** This event is fired just after an exam has been finished */ +public class ExamFinishedEvent extends ApplicationEvent { + + private static final long serialVersionUID = -1528880878532843063L; + + public final Exam exam; + + public ExamFinishedEvent(final Exam exam) { + super(exam); + this.exam = exam; + } +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamResetEvent.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamResetEvent.java new file mode 100644 index 00000000..440c1f72 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamResetEvent.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.session; + +import org.springframework.context.ApplicationEvent; + +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; + +public class ExamResetEvent extends ApplicationEvent { + + private static final long serialVersionUID = -2854284031889020212L; + + public final Exam exam; + + public ExamResetEvent(final Exam exam) { + super(exam); + this.exam = exam; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamSessionService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamSessionService.java index 7091a992..38679835 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamSessionService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamSessionService.java @@ -77,7 +77,7 @@ public interface ExamSessionService { /** Use this to check if a specified Exam has currently active SEB Client connections. * * Active SEB Client connections are established connections that are not yet closed and - * connection attempts that are older the a defined time interval. + * open connection attempts. * * @param examId The Exam identifier * @return true if the given Exam has currently no active client connection, false otherwise. */ @@ -86,7 +86,7 @@ public interface ExamSessionService { return false; } - return !this.getActiveConnectionTokens(examId) + return !this.getAllActiveConnectionTokens(examId) .getOrThrow() .isEmpty(); } @@ -134,6 +134,15 @@ public interface ExamSessionService { FilterMap filterMap, Predicate predicate); + /** Gets all finished Exams for a particular FilterMap. + * + * @param filterMap the FilterMap containing the filter attributes + * @param predicate additional filter predicate + * @return Result referencing the list of all currently finished Exams or to an error if happened. */ + Result> getFilteredFinishedExams( + FilterMap filterMap, + Predicate predicate); + /** Streams the default SEB Exam Configuration to a ClientConnection with given connectionToken. * * @param institutionId the Institution identifier @@ -175,19 +184,20 @@ public interface ExamSessionService { final Long examId, final Predicate filter); - /** Gets all connection tokens of active client connection that are related to a specified exam + /** Gets all connection tokens of client connection that are in ACTIVE state and related to a specified exam * from persistence storage without caching involved. * * @param examId the exam identifier * @return Result refer to the collection of connection tokens or to an error when happened. */ Result> getActiveConnectionTokens(Long examId); - /** Called to notify that the given exam has just been finished. - * This cleanup all exam session caches for the given exam and also cleanup session based stores on the persistent. + /** Gets all connection tokens of client connections that are in an active state. See ClientConnection + * And that are related to a specified exam. + * There is no caching involved here, gets actual data from persistent storage * - * @param exam the Exam that has just been finished - * @return Result refer to the finished exam or to an error when happened. */ - Result notifyExamFinished(final Exam exam); + * @param examId the exam identifier + * @return Result refer to the collection of connection tokens or to an error when happened. */ + Result> getAllActiveConnectionTokens(Long examId); /** Use this to check if the current cached running exam is up to date * and if not to flush the cache. @@ -208,7 +218,7 @@ public interface ExamSessionService { * @return Result with reference to the given Exam or to an error if happened */ Result flushCache(final Exam exam); - /** Is is supposed to be the single access point to internally get client connection + /** This is supposed to be the single access point to internally get client connection * data for a specified connection token. * This uses the client connection data cache for lookup and also synchronizes asynchronous * cache calls to prevent parallel creation of ClientConnectionDataInternal diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamStartedEvent.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamStartedEvent.java new file mode 100644 index 00000000..ee6ac689 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamStartedEvent.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.session; + +import org.springframework.context.ApplicationEvent; + +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; + +/** This event is fired just after an exam has been started */ +public class ExamStartedEvent extends ApplicationEvent { + + private static final long serialVersionUID = -6564345490588661010L; + + public final Exam exam; + + public ExamStartedEvent(final Exam exam) { + super(exam); + this.exam = exam; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/SEBClientConnectionService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/SEBClientConnectionService.java index f03105c0..c8e3d960 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/SEBClientConnectionService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/SEBClientConnectionService.java @@ -13,6 +13,7 @@ import java.util.Collection; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; +import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -184,4 +185,11 @@ public interface SEBClientConnectionService { * @param instructionConfirm the instruction confirm identifier */ void confirmInstructionDone(String connectionToken, String instructionConfirm); + /** Use this to get the get the specific indicator values for a given client connection. + * + * @param clientConnection The client connection values + * @return Result refer to ClientConnectionData instance containing the given clientConnection plus the indicator + * values or to an error when happened */ + Result getIndicatorValues(final ClientConnection clientConnection); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientIndicatorFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientIndicatorFactory.java index 5aee4fec..80333c5d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientIndicatorFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientIndicatorFactory.java @@ -25,6 +25,7 @@ import org.springframework.stereotype.Component; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; +import ch.ethz.seb.sebserver.gbl.model.session.IndicatorValue; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.IndicatorDAO; @@ -100,6 +101,43 @@ public class ClientIndicatorFactory { } } + public List getIndicatorValues(final ClientConnection clientConnection) { + + final List result = new ArrayList<>(); + if (clientConnection.examId == null) { + return result; + } + + try { + + final Collection examIndicators = this.indicatorDAO + .allForExam(clientConnection.examId) + .getOrThrow(); + + for (final Indicator indicatorDef : examIndicators) { + try { + + final ClientIndicator indicator = this.applicationContext + .getBean(indicatorDef.type.name(), ClientIndicator.class); + + indicator.init( + indicatorDef, + clientConnection.id, + clientConnection.status.clientActiveStatus, + true); + + result.add(indicator); + } catch (final Exception e) { + log.error("Failed to create IndicatorValue for indicator: {}", indicatorDef, e); + } + } + } catch (final Exception e) { + log.error("Failed to create IndicatorValues for ClientConnection: {}", clientConnection); + } + + return Collections.unmodifiableList(result); + } + public List createFor(final ClientConnection clientConnection) { final List result = new ArrayList<>(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java index 3d673263..f414d798 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java @@ -75,7 +75,7 @@ public class ExamSessionCacheService { log.debug("Verify running exam for id: {}", examId); } - final Result byPK = this.examDAO.loadWithAdditionalAttributes(examId); + final Result byPK = this.examDAO.byPK(examId); if (byPK.hasError()) { log.error("Failed to find/load Exam with id {}", examId, byPK.getError()); return null; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java index 5e4a4fb7..ac43a2e6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java @@ -8,8 +8,10 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; -import java.util.stream.Collectors; +import java.util.Set; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -29,7 +31,6 @@ import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringRoomService; -import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientConnectionService; @Service @@ -43,7 +44,6 @@ public class ExamSessionControlTask implements DisposableBean { private final ExamUpdateHandler examUpdateHandler; private final ExamProctoringRoomService examProcotringRoomService; private final WebserviceInfo webserviceInfo; - private final ExamSessionService examSessionService; private final Long examTimePrefix; private final Long examTimeSuffix; @@ -56,7 +56,6 @@ public class ExamSessionControlTask implements DisposableBean { final ExamUpdateHandler examUpdateHandler, final ExamProctoringRoomService examProcotringRoomService, final WebserviceInfo webserviceInfo, - final ExamSessionService examSessionService, @Value("${sebserver.webservice.api.exam.time-prefix:3600000}") final Long examTimePrefix, @Value("${sebserver.webservice.api.exam.time-suffix:3600000}") final Long examTimeSuffix, @Value("${sebserver.webservice.api.exam.update-interval:1 * * * * *}") final String examTaskCron, @@ -66,7 +65,6 @@ public class ExamSessionControlTask implements DisposableBean { this.sebClientConnectionService = sebClientConnectionService; this.examUpdateHandler = examUpdateHandler; this.webserviceInfo = webserviceInfo; - this.examSessionService = examSessionService; this.examTimePrefix = examTimePrefix; this.examTimeSuffix = examTimeSuffix; this.examTaskCron = examTaskCron; @@ -75,7 +73,7 @@ public class ExamSessionControlTask implements DisposableBean { } @EventListener(SEBServerInitEvent.class) - public void init() { + private void init() { SEBServerInit.INIT_LOGGER.info("------>"); SEBServerInit.INIT_LOGGER.info("------> Activate exam run controller background task"); SEBServerInit.INIT_LOGGER.info("--------> Task runs on an cron-job interval of {}", this.examTaskCron); @@ -95,7 +93,7 @@ public class ExamSessionControlTask implements DisposableBean { @Scheduled( fixedDelayString = "${sebserver.webservice.api.exam.update-interval:60000}", initialDelay = 10000) - public void examRunUpdateTask() { + private void examRunUpdateTask() { if (!this.webserviceInfo.isMaster()) { return; @@ -107,15 +105,15 @@ public class ExamSessionControlTask implements DisposableBean { log.debug("Run exam update task with Id: {}", updateId); } - controlExamStart(updateId); - controlExamEnd(updateId); + controlExamLMSUpdate(); + controlExamState(updateId); this.examDAO.releaseAgedLocks(); } @Scheduled( fixedDelayString = "${sebserver.webservice.api.seb.lostping.update:5000}", initialDelay = 5000) - public void examSessionUpdateTask() { + private void examSessionUpdateTask() { updateMaster(); @@ -136,7 +134,7 @@ public class ExamSessionControlTask implements DisposableBean { @Scheduled( fixedRateString = "${sebserver.webservice.api.exam.session-cleanup:30000}", initialDelay = 30000) - public void examSessionCleanupTask() { + private void examSessionCleanupTask() { if (!this.webserviceInfo.isMaster()) { return; @@ -149,7 +147,49 @@ public class ExamSessionControlTask implements DisposableBean { this.sebClientConnectionService.cleanupInstructions(); } - private void controlExamStart(final String updateId) { + private void controlExamLMSUpdate() { + if (log.isTraceEnabled()) { + log.trace("Start update exams from LMS"); + } + + try { + + // create mapping + final Map> examToLMSMapping = new HashMap<>(); + this.examDAO.allForLMSUpdate() + .onError(error -> log.error("Failed to update exams from LMS: ", error)) + .getOr(Collections.emptyList()) + .stream() + .forEach(exam -> { + final Map examMap = (examToLMSMapping.computeIfAbsent( + exam.lmsSetupId, + lmsId -> new HashMap<>())); + examMap.put(exam.externalId, exam); + }); + + // update per LMS Setup + examToLMSMapping.entrySet() + .stream() + .forEach(updateEntry -> { + final Result> updateExamFromLMS = this.examUpdateHandler + .updateExamFromLMS(updateEntry.getKey(), updateEntry.getValue()); + + if (updateExamFromLMS.hasError()) { + log.error("Failed to update exams from LMS: ", updateExamFromLMS.getError()); + } else { + final Set failedExams = updateExamFromLMS.get(); + if (!failedExams.isEmpty()) { + log.warn("Failed to update following exams from LMS: {}", failedExams); + } + } + }); + + } catch (final Exception e) { + log.error("Unexpected error while update exams from LMS: ", e); + } + } + + private void controlExamState(final String updateId) { if (log.isTraceEnabled()) { log.trace("Check starting exams: {}", updateId); } @@ -157,47 +197,19 @@ public class ExamSessionControlTask implements DisposableBean { try { final DateTime now = DateTime.now(DateTimeZone.UTC); - final Map updated = this.examDAO.allForRunCheck() + this.examDAO + .allThatNeedsStatusUpdate(this.examTimePrefix, this.examTimeSuffix) .getOrThrow() .stream() - .filter(exam -> exam.startTime.minus(this.examTimePrefix).isBefore(now)) - .filter(exam -> exam.endTime == null || exam.endTime.plus(this.examTimeSuffix).isAfter(now)) - .flatMap(exam -> Result.skipOnError(this.examUpdateHandler.setRunning(exam, updateId))) - .collect(Collectors.toMap(Exam::getId, Exam::getName)); - - if (!updated.isEmpty()) { - log.info("Updated exams to running state: {}", updated); - } + .forEach(exam -> this.examUpdateHandler.updateState( + exam, + now, + this.examTimePrefix, + this.examTimeSuffix, + updateId)); } catch (final Exception e) { - log.error("Unexpected error while trying to update exams: ", e); - } - } - - private void controlExamEnd(final String updateId) { - if (log.isTraceEnabled()) { - log.trace("Check ending exams: {}", updateId); - } - - try { - - final DateTime now = DateTime.now(DateTimeZone.UTC); - - final Map updated = this.examDAO.allForEndCheck() - .getOrThrow() - .stream() - .filter(exam -> exam.endTime != null && exam.endTime.plus(this.examTimeSuffix).isBefore(now)) - .flatMap(exam -> Result.skipOnError(this.examUpdateHandler.setFinished(exam, updateId))) - .flatMap(exam -> Result.skipOnError(this.examProcotringRoomService.disposeRoomsForExam(exam))) - .flatMap(exam -> Result.skipOnError(this.examSessionService.notifyExamFinished(exam))) - .collect(Collectors.toMap(Exam::getId, Exam::getName)); - - if (!updated.isEmpty()) { - log.info("Updated exams to finished state: {}", updated); - } - - } catch (final Exception e) { - log.error("Unexpected error while trying to update exams: ", e); + log.error("Unexpected error while trying to run exam state update task: ", e); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java index 2cb520c0..84be861e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java @@ -26,12 +26,14 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Lazy; +import org.springframework.context.event.EventListener; import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Service; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage; +import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; @@ -47,6 +49,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.IndicatorDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamFinishedEvent; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamResetEvent; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; @Lazy @@ -123,19 +127,14 @@ public class ExamSessionServiceImpl implements ExamSessionService { return Result.tryCatch(() -> { final Collection result = new ArrayList<>(); - final Exam exam = (this.isExamRunning(examId)) - ? this.examSessionCacheService.getRunningExam(examId) - : this.examDAO - .byPK(examId) - .getOrThrow(); + final Exam exam = this.examDAO + .byPK(examId) + .getOrThrow(); // check lms connection - if (exam.status == ExamStatus.CORRUPT_NO_LMS_CONNECTION) { + if (!exam.isLmsAvailable()) { result.add(ErrorMessage.EXAM_CONSISTENCY_VALIDATION_LMS_CONNECTION.of(exam.getModelId())); } - if (exam.status == ExamStatus.CORRUPT_INVALID_ID) { - result.add(ErrorMessage.EXAM_CONSISTENCY_VALIDATION_INVALID_ID_REFERENCE.of(exam.getModelId())); - } if (exam.status == ExamStatus.RUNNING) { // check exam supporter @@ -243,17 +242,39 @@ public class ExamSessionServiceImpl implements ExamSessionService { .putIfAbsent(Exam.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING) .putIfAbsent(Exam.FILTER_ATTR_STATUS, ExamStatus.RUNNING.name()); - // NOTE: we evict the exam from the cache (if present) to ensure user is seeing always the current state of the Exam return this.examDAO.allMatching(filterMap, predicate) .map(col -> col.stream() .map(exam -> { - this.examSessionCacheService.evict(exam); - return this.examSessionCacheService.getRunningExam(exam.id); + final Exam runningExam = this.examSessionCacheService.getRunningExam(exam.id); + if (runningExam == null) { + return null; + } + if (!isUpToDate(exam, runningExam)) { + // If the cached exam-quiz data differs from the one of the currently loaded exam, cache is updated + this.examSessionCacheService.evict(exam); + return this.examSessionCacheService.getRunningExam(exam.id); + } else { + return runningExam; + } }) .filter(Objects::nonNull) .collect(Collectors.toList())); } + @Override + public Result> getFilteredFinishedExams( + final FilterMap filterMap, + final Predicate predicate) { + + filterMap.putIfAbsent(Entity.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING); + + return this.examDAO.getExamsForStatus( + filterMap, + predicate, + ExamStatus.FINISHED, + ExamStatus.ARCHIVED); + } + @Override public void streamDefaultExamConfig( final Long institutionId, @@ -407,23 +428,50 @@ public class ExamSessionServiceImpl implements ExamSessionService { @Override public Result> getActiveConnectionTokens(final Long examId) { return this.clientConnectionDAO - .getActiveConnctionTokens(examId); + .getActiveConnectionTokens(examId); } @Override - public Result notifyExamFinished(final Exam exam) { - return Result.tryCatch(() -> { - if (!isExamRunning(exam.id)) { - this.flushCache(exam); + public Result> getAllActiveConnectionTokens(final Long examId) { + return this.clientConnectionDAO + .getAllActiveConnectionTokens(examId); + } + + @EventListener + public void notifyExamRest(final ExamResetEvent event) { + log.info("ExamResetEvent received, process exam session cleanup..."); + + try { + if (!isExamRunning(event.exam.id)) { + this.flushCache(event.exam); if (this.distributedSetup) { this.clientConnectionDAO - .deleteClientIndicatorValues(exam) + .deleteClientIndicatorValues(event.exam) .getOrThrow(); } } + } catch (final Exception e) { + log.error("Failed to cleanup on reset exam: {}", event.exam, e); + } + } - return exam; - }); + @EventListener + public void notifyExamFinished(final ExamFinishedEvent event) { + + log.info("ExamFinishedEvent received, process exam session cleanup..."); + + try { + if (!isExamRunning(event.exam.id)) { + this.flushCache(event.exam); + if (this.distributedSetup) { + this.clientConnectionDAO + .deleteClientIndicatorValues(event.exam) + .getOrThrow(); + } + } + } catch (final Exception e) { + log.error("Failed to cleanup on finished exam: {}", event.exam, e); + } } @Override @@ -500,4 +548,11 @@ public class ExamSessionServiceImpl implements ExamSessionService { } } + private boolean isUpToDate(final Exam exam, final Exam runningExam) { + return Objects.equals(exam.lastModified, runningExam.lastModified) + && Objects.equals(exam.startTime, runningExam.startTime) + && Objects.equals(exam.endTime, runningExam.endTime) + && Objects.equals(exam.name, runningExam.name); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamUpdateHandler.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamUpdateHandler.java index 3defad8d..a3dd4a3c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamUpdateHandler.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamUpdateHandler.java @@ -8,22 +8,40 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; +import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleCourseAccess; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamFinishedEvent; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamResetEvent; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamStartedEvent; @Lazy @Service @@ -33,18 +51,24 @@ class ExamUpdateHandler { private static final Logger log = LoggerFactory.getLogger(ExamUpdateHandler.class); private final ExamDAO examDAO; + private final ApplicationEventPublisher applicationEventPublisher; private final SEBRestrictionService sebRestrictionService; + private final LmsAPIService lmsAPIService; private final String updatePrefix; private final Long examTimeSuffix; public ExamUpdateHandler( final ExamDAO examDAO, + final ApplicationEventPublisher applicationEventPublisher, final SEBRestrictionService sebRestrictionService, + final LmsAPIService lmsAPIService, final WebserviceInfo webserviceInfo, @Value("${sebserver.webservice.api.exam.time-suffix:3600000}") final Long examTimeSuffix) { this.examDAO = examDAO; + this.applicationEventPublisher = applicationEventPublisher; this.sebRestrictionService = sebRestrictionService; + this.lmsAPIService = lmsAPIService; this.updatePrefix = webserviceInfo.getLocalHostAddress() + "_" + webserviceInfo.getServerPort() + "_"; this.examTimeSuffix = examTimeSuffix; @@ -58,6 +82,67 @@ class ExamUpdateHandler { return this.updatePrefix + Utils.getMillisecondsNow(); } + Result> updateExamFromLMS(final Long lmsSetupId, final Map exams) { + + return Result.tryCatch(() -> { + final Set failedOrMissing = new HashSet<>(exams.keySet()); + final String updateId = this.createUpdateId(); + + this.lmsAPIService + .getLmsAPITemplate(lmsSetupId) + .map(template -> { + // TODO flush only involved courses from cache! + template.clearCourseCache(); + return template; + }) + .flatMap(template -> template.getQuizzes(new HashSet<>(exams.keySet()))) + .onError(error -> log.warn( + "Failed to get quizzes from LMS Setup: {} cause: {}", + lmsSetupId, + error.getMessage())) + .getOr(Collections.emptyList()) + .stream() + .forEach(quiz -> { + + try { + final Exam exam = exams.get(quiz.id); + if (hasChanges(exam, quiz)) { + + final Result updateQuizData = this.examDAO + .updateQuizData(exam.id, quiz, updateId); + + if (updateQuizData.hasError()) { + log.error("Failed to update quiz data for exam: {}", quiz, + updateQuizData.getError()); + } else { + if (!exam.isLmsAvailable()) { + this.examDAO.markLMSAvailability(quiz.id, true, updateId); + } + failedOrMissing.remove(quiz.id); + log.info("Updated quiz data for exam: {}", updateQuizData.get()); + } + + } else { + if (!exam.isLmsAvailable()) { + this.examDAO.markLMSAvailability(quiz.id, true, updateId); + } + failedOrMissing.remove(quiz.id); + } + } catch (final Exception e) { + log.error("Unexpected error while trying to update quiz data for exam: {}", quiz, e); + } + }); + + if (!failedOrMissing.isEmpty()) { + new HashSet<>(failedOrMissing).stream() + .forEach(quizId -> tryRecoverQuizData(quizId, lmsSetupId, exams, updateId) + .onSuccess(quizData -> failedOrMissing.remove(quizId))); + } + + return failedOrMissing; + }); + } + Result updateRunning(final Long examId) { return this.examDAO.byPK(examId) .map(exam -> { @@ -72,6 +157,113 @@ class ExamUpdateHandler { }); } + void updateState( + final Exam exam, + final DateTime now, + final long leadTime, + final long followupTime, + final String updateId) { + + try { + // Include leadTime and followupTime + final DateTime startTimeThreshold = now.plus(leadTime); + final DateTime endTimeThreshold = now.minus(leadTime); + + if (log.isDebugEnabled()) { + log.debug("Check exam update for startTimeThreshold: {}, endTimeThreshold {}, exam: {}", + startTimeThreshold, + endTimeThreshold, + exam); + } + + if (exam.status == ExamStatus.ARCHIVED) { + log.warn("Exam in unexpected state for status update. Skip update. Exam: {}", exam); + return; + } + + if (exam.status != ExamStatus.RUNNING && withinTimeframe( + exam.startTime, + startTimeThreshold, + exam.endTime, + endTimeThreshold)) { + + if (withinTimeframe(exam.startTime, startTimeThreshold, exam.endTime, endTimeThreshold)) { + setRunning(exam, updateId) + .onError(error -> log.error("Failed to update exam to running state: {}", + exam, + error)); + return; + } + } + + if (exam.status != ExamStatus.FINISHED && + exam.endTime != null && + endTimeThreshold.isAfter(exam.endTime)) { + setFinished(exam, updateId) + .onError(error -> log.error("Failed to update exam to finished state: {}", + exam, + error)); + return; + } + + if (exam.status != ExamStatus.UP_COMING && + exam.startTime != null && + startTimeThreshold.isBefore(exam.startTime)) { + setUpcoming(exam, updateId) + .onError(error -> log.error("Failed to update exam to up-coming state: {}", + exam, + error)); + } + } catch (final Exception e) { + log.error("Unexpected error while trying to update exam state for exam: {}", exam, e); + } + } + + private boolean withinTimeframe( + final DateTime startTime, + final DateTime startTimeThreshold, + final DateTime endTime, + final DateTime endTimeThreshold) { + + if (startTime == null && endTime == null) { + return true; + } + + if (startTime == null && endTime.isAfter(endTimeThreshold)) { + return true; + } + + if (endTime == null && startTime.isBefore(startTimeThreshold)) { + return true; + } + + return (startTime.isBefore(startTimeThreshold) && endTime.isAfter(endTimeThreshold)); + } + + Result setUpcoming(final Exam exam, final String updateId) { + if (log.isDebugEnabled()) { + log.debug("Update exam as up-coming: {}", exam); + } + + return this.examDAO + .placeLock(exam.id, updateId) + .flatMap(e -> this.examDAO.updateState(exam.id, ExamStatus.UP_COMING, updateId)) + .map(e -> { + this.examDAO + .releaseLock(e, updateId) + .onError(error -> this.examDAO + .forceUnlock(exam.id) + .onError(unlockError -> log.error( + "Failed to force unlock update look for exam: {}", + exam.id))); + return e; + }) + .map(e -> { + this.applicationEventPublisher.publishEvent(new ExamResetEvent(exam)); + return exam; + }); + } + Result setRunning(final Exam exam, final String updateId) { if (log.isDebugEnabled()) { log.debug("Update exam as running: {}", exam); @@ -79,14 +271,21 @@ class ExamUpdateHandler { return this.examDAO .placeLock(exam.id, updateId) - .flatMap(e -> this.examDAO.updateState( - exam.id, - ExamStatus.RUNNING, - updateId)) - .flatMap(this.sebRestrictionService::applySEBClientRestriction) - .flatMap(e -> this.examDAO.releaseLock(e, updateId)) - .onError(error -> this.examDAO.forceUnlock(exam.id) - .onError(unlockError -> log.error("Failed to force unlock update look for exam: {}", exam.id))); + .flatMap(e -> this.examDAO.updateState(exam.id, ExamStatus.RUNNING, updateId)) + .map(e -> { + this.examDAO + .releaseLock(e, updateId) + .onError(error -> this.examDAO + .forceUnlock(exam.id) + .onError(unlockError -> log.error( + "Failed to force unlock update look for exam: {}", + exam.id))); + return e; + }) + .map(e -> { + this.applicationEventPublisher.publishEvent(new ExamStartedEvent(exam)); + return exam; + }); } Result setFinished(final Exam exam, final String updateId) { @@ -96,13 +295,113 @@ class ExamUpdateHandler { return this.examDAO .placeLock(exam.id, updateId) - .flatMap(e -> this.examDAO.updateState( - exam.id, - ExamStatus.FINISHED, - updateId)) - .flatMap(this.sebRestrictionService::releaseSEBClientRestriction) - .flatMap(e -> this.examDAO.releaseLock(e, updateId)) - .onError(error -> this.examDAO.forceUnlock(exam.id)); + .flatMap(e -> this.examDAO.updateState(exam.id, ExamStatus.FINISHED, updateId)) + .map(e -> { + this.examDAO + .releaseLock(e, updateId) + .onError(error -> this.examDAO + .forceUnlock(exam.id) + .onError(unlockError -> log.error( + "Failed to force unlock update look for exam: {}", + exam.id))); + return e; + }) + .map(e -> { + this.applicationEventPublisher.publishEvent(new ExamFinishedEvent(exam)); + return exam; + }); + } + + private boolean hasChanges(final Exam exam, final QuizData quizData) { + if (!Objects.equals(exam.name, quizData.name) || + !Objects.equals(exam.startTime, quizData.startTime) || + !Objects.equals(exam.endTime, quizData.endTime) || + !Objects.equals(exam.getDescription(), quizData.description) || + !Objects.equals(exam.getStartURL(), quizData.startURL)) { + + return true; + } + + if (quizData.additionalAttributes != null && !quizData.additionalAttributes.isEmpty()) { + for (final Map.Entry attr : quizData.additionalAttributes.entrySet()) { + if (!Objects.equals(exam.getAdditionalAttribute(attr.getKey()), attr.getValue())) { + return true; + } + } + } + + return false; + } + + private Result tryRecoverQuizData( + final String quizId, + final Long lmsSetupId, + final Map exams, + final String updateId) { + + return Result.tryCatch(() -> { + final Exam exam = exams.get(quizId); + final LmsAPITemplate lmsTemplate = this.lmsAPIService + .getLmsAPITemplate(lmsSetupId) + .getOrThrow(); + + // If this is a Moodle quiz, try to recover from eventually restore of the quiz on the LMS side + // NOTE: This is a workaround for Moodle quizzes that had have a recovery within the sandbox tool + // Where potentially quiz identifiers get changed during such a recovery and the SEB Server + // internal mapping is not working properly anymore. In this case we try to recover from such + // a case by using the short name of the quiz and search for the quiz within the course with this + // short name. If one quiz has been found that matches all criteria, we adapt the internal id + // mapping to this quiz. + // If recovering fails, this returns null and the calling side must handle the lack of quiz data + if (lmsTemplate.getType() == LmsType.MOODLE) { + + log.info("Try to recover quiz data for Moodle quiz with internal identifier: {}", quizId); + + if (exam != null && exam.name != null + && !exam.name.startsWith(Constants.SQUARE_BRACE_OPEN.toString())) { + + log.debug("Found formerName quiz name: {}", exam.name); + + // get the course name identifier + final String shortname = MoodleCourseAccess.getShortname(quizId); + if (StringUtils.isNotBlank(shortname)) { + + log.debug("Using short-name: {} for recovering", shortname); + + final QuizData recoveredQuizData = lmsTemplate + .getQuizzes(new FilterMap()) + .getOrThrow() + .stream() + .filter(quiz -> { + final String qShortName = MoodleCourseAccess.getShortname(quiz.id); + return qShortName != null && qShortName.equals(shortname); + }) + .filter(quiz -> exam.name.equals(quiz.name)) + .findAny() + .get(); + + if (recoveredQuizData != null) { + + log.debug("Found quiz data for recovering: {}", recoveredQuizData); + + // save exam with new external id and quit data + this.examDAO + .updateQuizData(exam.id, recoveredQuizData, updateId) + .getOrThrow(); + + log.debug("Successfully recovered exam quiz data to new externalId {}", + recoveredQuizData.id); + } + return recoveredQuizData; + } + } + } + + if (exam.lmsAvailable == null || exam.isLmsAvailable()) { + this.examDAO.markLMSAvailability(quizId, false, updateId); + } + throw new RuntimeException("Not Available"); + }); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java index 3e28fdde..9b1658d3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientConnectionServiceImpl.java @@ -11,9 +11,9 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; import java.math.BigDecimal; import java.security.Principal; import java.util.Collection; +import java.util.Collections; import java.util.Objects; import java.util.UUID; -import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -22,9 +22,8 @@ import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Lazy; +import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Service; import ch.ethz.seb.sebserver.gbl.Constants; @@ -64,11 +63,11 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic ConnectionStatus.UNDEFINED, ConnectionStatus.CONNECTION_REQUESTED, ConnectionStatus.AUTHENTICATED, + ConnectionStatus.ACTIVE, ConnectionStatus.CLOSED); private final ExamSessionService examSessionService; private final ExamSessionCacheService examSessionCacheService; - private final CacheManager cacheManager; private final EventHandlingStrategy eventHandlingStrategy; private final ClientConnectionDAO clientConnectionDAO; private final SEBClientConfigDAO sebClientConfigDAO; @@ -89,15 +88,14 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic this.examSessionService = examSessionService; this.examSessionCacheService = examSessionService.getExamSessionCacheService(); - this.cacheManager = examSessionService.getCacheManager(); this.clientConnectionDAO = examSessionService.getClientConnectionDAO(); this.eventHandlingStrategy = eventHandlingStrategyFactory.get(); this.sebClientConfigDAO = sebClientConfigDAO; this.sebInstructionService = sebInstructionService; this.examAdminService = examAdminService; + this.clientIndicatorFactory = clientIndicatorFactory; this.distributedPingCache = distributedPingCache; this.isDistributedSetup = sebInstructionService.getWebserviceInfo().isDistributed(); - this.clientIndicatorFactory = clientIndicatorFactory; } @Override @@ -120,7 +118,13 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic final SEBClientConfig clientConfig = this.sebClientConfigDAO .byClientName(principal.getName()) - .getOrThrow(); + .getOr(null); + + if (clientConfig == null) { + log.error("Illegal client connection request: requested connection config name: {}", + principal.getName()); + throw new AccessDeniedException("Unknown or illegal client access"); + } if (!clientConfig.institutionId.equals(institutionId)) { log.error("Institutional integrity violation: requested institution: {} authenticated institution: {}", @@ -142,7 +146,11 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic } if (examId != null) { - checkExamIntegrity(examId); + checkExamIntegrity( + examId, + institutionId, + (principal != null) ? principal.getName() : "--", + clientAddress); } // Create ClientConnection in status CONNECTION_REQUESTED for further processing @@ -172,6 +180,11 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic this.clientIndicatorFactory.initializeDistributedCaches(clientConnection); } + // flash connection token cache for exam if available + if (examId != null) { + this.clientConnectionDAO.evictConnectionTokenCache(examId); + } + // load client connection data into cache final ClientConnectionDataInternal activeClientConnection = this.examSessionService .getConnectionDataInternal(connectionToken); @@ -226,7 +239,11 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic } if (examId != null) { - checkExamIntegrity(examId); + checkExamIntegrity( + examId, + institutionId, + StringUtils.isNoneBlank(userSessionId) ? userSessionId : clientConnection.userSessionId, + clientConnection.clientAddress); } if (log.isDebugEnabled()) { @@ -274,8 +291,9 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic this.clientIndicatorFactory.initializeDistributedCaches(clientConnection); } - final ClientConnectionDataInternal activeClientConnection = - reloadConnectionCache(connectionToken); + final ClientConnectionDataInternal activeClientConnection = reloadConnectionCache( + connectionToken, + examId); if (activeClientConnection == null) { log.warn("Failed to load ClientConnectionDataInternal into cache on update"); @@ -412,7 +430,11 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic .getOrThrow(); // check exam integrity for established connection - checkExamIntegrity(establishedClientConnection.examId); + checkExamIntegrity( + establishedClientConnection.examId, + institutionId, + establishedClientConnection.userSessionId, + establishedClientConnection.clientAddress); // initialize distributed indicator value caches if possible and needed if (examId != null && this.isDistributedSetup) { @@ -425,8 +447,9 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic } // flush and reload caches to work with actual connection data - final ClientConnectionDataInternal activeClientConnection = - reloadConnectionCache(connectionToken); + final ClientConnectionDataInternal activeClientConnection = reloadConnectionCache( + connectionToken, + examId); if (activeClientConnection == null) { log.warn("Failed to load ClientConnectionDataInternal into cache on update"); @@ -480,13 +503,14 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic establishedClientConnection.remoteProctoringRoomUpdate); // Update other connection with token and exam id - this.clientConnectionDAO + final ClientConnection connection = this.clientConnectionDAO .save(new ClientConnection( vdiPairCompanion.getId(), null, vdiExamId, null, null, null, null, null, null, establishedClientConnection.connectionToken, null, null, null, null, null, null, null)) .getOrThrow(); - reloadConnectionCache(vdiPairCompanion.getConnectionToken()); + + reloadConnectionCache(vdiPairCompanion.getConnectionToken(), connection.examId); return updatedConnection; } @@ -541,7 +565,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic .deleteIndicatorValues(updatedClientConnection.id); } - reloadConnectionCache(connectionToken); + reloadConnectionCache(connectionToken, clientConnection.examId); return updatedClientConnection; }); } @@ -603,7 +627,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic .deleteIndicatorValues(updatedClientConnection.id); } - reloadConnectionCache(connectionToken); + reloadConnectionCache(connectionToken, clientConnection.examId); return updatedClientConnection; }); } @@ -626,28 +650,19 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic public void updatePingEvents() { try { - final Cache cache = this.cacheManager.getCache(ExamSessionCacheService.CACHE_NAME_ACTIVE_CLIENT_CONNECTION); - final long now = Utils.getMillisecondsNow(); - final Consumer missingPingUpdate = missingPingUpdate(now); this.examSessionService .getExamDAO() .allRunningExamIds() .getOrThrow() .stream() - .flatMap(examId -> this.isDistributedSetup - ? this.clientConnectionDAO - .getConnectionTokensNoCache(examId) - .getOrThrow() - .stream() - : this.clientConnectionDAO - .getConnectionTokens(examId) - .getOrThrow() - .stream()) - .map(token -> cache.get(token, ClientConnectionDataInternal.class)) + .flatMap(examId -> this.clientConnectionDAO + .getAllActiveConnectionTokens(examId) + .getOr(Collections.emptyList()) + .stream()) + .map(this.examSessionService::getConnectionDataInternal) .filter(Objects::nonNull) - .filter(connection -> connection.pingIndicator != null && - connection.clientConnection.status.clientActiveStatus) - .forEach(connection -> missingPingUpdate.accept(connection)); + .filter(connection -> connection.pingIndicator != null) + .forEach(this::missingPingUpdate); } catch (final Exception e) { log.error("Failed to update ping events: ", e); @@ -719,9 +734,16 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic this.sebInstructionService.confirmInstructionDone(connectionToken, instructionConfirm); } - private void checkExamRunning(final Long examId) { + @Override + public Result getIndicatorValues(final ClientConnection clientConnection) { + return Result.tryCatch(() -> new ClientConnectionData( + clientConnection, + this.clientIndicatorFactory.getIndicatorValues(clientConnection))); + } + + private void checkExamRunning(final Long examId, final String user, final String address) { if (examId != null && !this.examSessionService.isExamRunning(examId)) { - examNotRunningException(examId); + examNotRunningException(examId, user, address); } } @@ -747,9 +769,10 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic return UUID.randomUUID().toString(); } - private void examNotRunningException(final Long examId) { - log.error("The exam {} is not running", examId); - throw new IllegalStateException("The exam " + examId + " is not running"); + private void examNotRunningException(final Long examId, final String user, final String address) { + log.warn("The exam {} is not running. Called by: {} | on: {}", examId, user, address); + throw new APIConstraintViolationException( + "The exam " + examId + " is not running"); } private void checkExamIntegrity(final Long examId, final ClientConnection clientConnection) { @@ -762,7 +785,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic throw new IllegalArgumentException( "Exam integrity violation: another examId is already set for the connection"); } - checkExamRunning(examId); + checkExamRunning(examId, clientConnection.userSessionId, clientConnection.clientAddress); } private ClientConnection updateUserSessionId( @@ -821,7 +844,12 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic return clientConnection; } - private void checkExamIntegrity(final Long examId) { + private void checkExamIntegrity( + final Long examId, + final Long institutionId, + final String user, + final String address) { + if (this.isDistributedSetup) { // if the cached Exam is not up to date anymore, we have to update the cache first final Result updateExamCache = this.examSessionService.updateExamCache(examId); @@ -831,12 +859,22 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic } // check Exam is running and not locked - checkExamRunning(examId); + checkExamRunning(examId, user, address); if (this.examSessionService.isExamLocked(examId)) { throw new APIConstraintViolationException( "Exam is currently on update and locked for new SEB Client connections"); } + // check Exam is within the correct institution + if (this.examSessionService.getRunningExam(examId) + .map(e -> !e.institutionId.equals(institutionId)) + .onError(error -> log.error("Failed to get running exam: ", error)) + .getOr(true)) { + + throw new APIConstraintViolationException( + "Exam institution mismatch. The requested exam is not within the expected institution"); + } + // check Exam has a default SEB Exam configuration attached if (!this.examSessionService.hasDefaultConfigurationAttached(examId)) { throw new APIConstraintViolationException( @@ -856,38 +894,43 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic .getOrThrow(); } - private ClientConnectionDataInternal reloadConnectionCache(final String connectionToken) { + private ClientConnectionDataInternal reloadConnectionCache( + final String connectionToken, + final Long examId) { + + if (examId != null) { + // evict connection tokens for exam + this.clientConnectionDAO.evictConnectionTokenCache(examId); + } // evict cached ClientConnection this.examSessionCacheService.evictClientConnection(connectionToken); // and load updated ClientConnection into cache return this.examSessionService.getConnectionDataInternal(connectionToken); } - private Consumer missingPingUpdate(final long now) { + private void missingPingUpdate(final ClientConnectionDataInternal connection) { + if (connection.pingIndicator.changeOnIncident()) { - return connection -> { + final boolean missingPing = connection.getMissingPing(); + final long millisecondsNow = Utils.getMillisecondsNow(); + final ClientEventRecord clientEventRecord = new ClientEventRecord( + null, + connection.getConnectionId(), + (missingPing) ? EventType.ERROR_LOG.id : EventType.INFO_LOG.id, + millisecondsNow, + millisecondsNow, + new BigDecimal(connection.pingIndicator.getValue()), + (missingPing) ? "Missing Client Ping" : "Client Ping Back To Normal"); - if (connection.pingIndicator.missingPingUpdate(now)) { - final boolean missingPing = connection.getMissingPing(); - final ClientEventRecord clientEventRecord = new ClientEventRecord( - null, - connection.getConnectionId(), - (missingPing) ? EventType.ERROR_LOG.id : EventType.INFO_LOG.id, - now, - now, - new BigDecimal(connection.pingIndicator.getValue()), - (missingPing) ? "Missing Client Ping" : "Client Ping Back To Normal"); + // store event and and flush cache + this.eventHandlingStrategy.accept(clientEventRecord); - // store event and and flush cache - this.eventHandlingStrategy.accept(clientEventRecord); - - // update indicators - if (clientEventRecord.getType() != null && EventType.ERROR_LOG.id == clientEventRecord.getType()) { - connection.getIndicatorMapping(EventType.ERROR_LOG) - .forEach(indicator -> indicator.notifyValueChange(clientEventRecord)); - } + // update indicators + if (clientEventRecord.getType() != null && EventType.ERROR_LOG.id == clientEventRecord.getType()) { + connection.getIndicatorMapping(EventType.ERROR_LOG) + .forEach(indicator -> indicator.notifyValueChange(clientEventRecord)); } - }; + } } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractClientIndicator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractClientIndicator.java index 8473cc52..f1755bcb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractClientIndicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractClientIndicator.java @@ -21,7 +21,7 @@ public abstract class AbstractClientIndicator implements ClientIndicator { private static final Logger log = LoggerFactory.getLogger(AbstractClientIndicator.class); - protected final DistributedIndicatorValueService distributedPingCache; + protected final DistributedIndicatorValueService distributedIndicatorValueService; protected Long indicatorId = -1L; protected Long examId = -1L; @@ -38,9 +38,9 @@ public abstract class AbstractClientIndicator implements ClientIndicator { protected long lastUpdate = 0; - public AbstractClientIndicator(final DistributedIndicatorValueService distributedPingCache) { + public AbstractClientIndicator(final DistributedIndicatorValueService distributedIndicatorValueService) { super(); - this.distributedPingCache = distributedPingCache; + this.distributedIndicatorValueService = distributedIndicatorValueService; } @Override @@ -69,19 +69,10 @@ public abstract class AbstractClientIndicator implements ClientIndicator { this.cachingEnabled = cachingEnabled; if (!this.cachingEnabled && this.active) { - this.ditributedIndicatorValueRecordId = this.distributedPingCache + + this.ditributedIndicatorValueRecordId = this.distributedIndicatorValueService .getIndicatorForConnection(connectionId, getType()); -// if (this.ditributedIndicatorValueRecordId == null) { -// tryRecoverIndicatorRecord(); -// } -// try { -// this.ditributedIndicatorValueRecordId = this.distributedPingCache.initIndicatorForConnection( -// connectionId, -// getType(), -// initValue()); -// } catch (final Exception e) { -// tryRecoverIndicatorRecord(); -// } + } this.currentValue = computeValueAt(Utils.getMillisecondsNow()); @@ -89,12 +80,12 @@ public abstract class AbstractClientIndicator implements ClientIndicator { } protected void tryRecoverIndicatorRecord() { - this.ditributedIndicatorValueRecordId = this.distributedPingCache.getIndicatorForConnection( + this.ditributedIndicatorValueRecordId = this.distributedIndicatorValueService.getIndicatorForConnection( this.connectionId, getType()); - if (this.ditributedIndicatorValueRecordId == null) { - log.warn("Failed to recover from missing indicator value cache record: {} type: {}", + if (this.ditributedIndicatorValueRecordId == null && log.isDebugEnabled()) { + log.debug("Failed to recover from missing indicator value cache record: {} type: {}", this.connectionId, getType()); } @@ -123,18 +114,18 @@ public abstract class AbstractClientIndicator implements ClientIndicator { public double getValue() { if (this.initialized && !this.cachingEnabled && this.active - && this.lastUpdate != this.distributedPingCache.lastUpdate()) { + && this.lastUpdate != this.distributedIndicatorValueService.lastUpdate()) { if (this.ditributedIndicatorValueRecordId == null) { this.tryRecoverIndicatorRecord(); } - final Long indicatorValue = this.distributedPingCache + final Long indicatorValue = this.distributedIndicatorValueService .getIndicatorValue(this.ditributedIndicatorValueRecordId); if (indicatorValue != null) { this.currentValue = indicatorValue.doubleValue(); } - this.lastUpdate = this.distributedPingCache.lastUpdate(); + this.lastUpdate = this.distributedIndicatorValueService.lastUpdate(); } return this.currentValue; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogLevelCountIndicator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogLevelCountIndicator.java index 516ca0cd..769cacf1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogLevelCountIndicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogLevelCountIndicator.java @@ -75,7 +75,7 @@ public abstract class AbstractLogLevelCountIndicator extends AbstractLogIndicato // update active indicator value record on persistent when caching is not enabled if (this.active && this.ditributedIndicatorValueRecordId != null) { - this.distributedPingCache.updateIndicatorValue( + this.distributedIndicatorValueService.updateIndicatorValue( this.ditributedIndicatorValueRecordId, numberOfLogs.longValue()); } @@ -115,7 +115,7 @@ public abstract class AbstractLogLevelCountIndicator extends AbstractLogIndicato private void valueChanged(final String eventText) { if (this.tags == null || this.tags.length == 0 || hasTag(eventText)) { if (super.ditributedIndicatorValueRecordId != null) { - this.distributedPingCache.incrementIndicatorValue(super.ditributedIndicatorValueRecordId); + this.distributedIndicatorValueService.incrementIndicatorValue(super.ditributedIndicatorValueRecordId); } this.currentValue = getValue() + 1d; } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogNumberIndicator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogNumberIndicator.java index b2e1224a..2ad92b56 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogNumberIndicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogNumberIndicator.java @@ -56,7 +56,7 @@ public abstract class AbstractLogNumberIndicator extends AbstractLogIndicator { private void valueChanged(final String text, final double value) { if (this.tags == null || this.tags.length == 0 || hasTag(text)) { if (super.ditributedIndicatorValueRecordId != null) { - if (!this.distributedPingCache.updateIndicatorValueAsync( + if (!this.distributedIndicatorValueService.updateIndicatorValueAsync( this.ditributedIndicatorValueRecordId, Double.valueOf(value).longValue())) { @@ -100,7 +100,7 @@ public abstract class AbstractLogNumberIndicator extends AbstractLogIndicator { // update active indicator value record on persistent when caching is not enabled if (this.active && this.ditributedIndicatorValueRecordId != null) { - this.distributedPingCache.updateIndicatorValue( + this.distributedIndicatorValueService.updateIndicatorValue( this.ditributedIndicatorValueRecordId, numericValue.longValue()); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractPingIndicator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractPingIndicator.java index 4987c509..14d0ed5b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractPingIndicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractPingIndicator.java @@ -12,7 +12,6 @@ import java.util.Collections; import java.util.EnumSet; import java.util.Set; -import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType; public abstract class AbstractPingIndicator extends AbstractClientIndicator { @@ -23,16 +22,6 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator { super(distributedPingCache); } - @Override - public void init( - final Indicator indicatorDefinition, - final Long connectionId, - final boolean active, - final boolean cachingEnabled) { - - super.init(indicatorDefinition, connectionId, active, cachingEnabled); - } - @Override public Set observedEvents() { return this.EMPTY_SET; @@ -41,16 +30,8 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator { public final void notifyPing(final long timestamp, final int pingNumber) { super.currentValue = timestamp; - if (!this.cachingEnabled) { - - if (super.ditributedIndicatorValueRecordId == null) { - tryRecoverIndicatorRecord(); - if (this.ditributedIndicatorValueRecordId == null) { - return; - } - } - - this.distributedPingCache.updatePingAsync(this.ditributedIndicatorValueRecordId); + if (!this.cachingEnabled && super.ditributedIndicatorValueRecordId != null) { + this.distributedIndicatorValueService.updatePingAsync(this.ditributedIndicatorValueRecordId); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/DistributedIndicatorValueService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/DistributedIndicatorValueService.java index 48519627..8e564645 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/DistributedIndicatorValueService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/DistributedIndicatorValueService.java @@ -154,11 +154,18 @@ public class DistributedIndicatorValueService implements DisposableBean { final Long recId = this.clientIndicatorValueMapper.indicatorRecordIdByConnectionId( connectionId, type); + if (recId != null) { - log.debug("Distributed indicator value cache already exists for: {}, {}", connectionId, type); + if (log.isTraceEnabled()) { + log.trace("Distributed indicator value cache already exists for: {}, {}", connectionId, type); + } return recId; } + if (log.isDebugEnabled()) { + log.info("Missing distributed indicator value cache. Create for: {}, {}", connectionId, type); + } + // if not, create new one and return PK final ClientIndicatorRecord clientEventRecord = new ClientIndicatorRecord( null, connectionId, type.id, initValue); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/PingIntervalClientIndicator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/PingIntervalClientIndicator.java index ffdb07e6..dbd09771 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/PingIntervalClientIndicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/PingIntervalClientIndicator.java @@ -34,8 +34,8 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator { private boolean hidden = false; - public PingIntervalClientIndicator(final DistributedIndicatorValueService distributedPingCache) { - super(distributedPingCache); + public PingIntervalClientIndicator(final DistributedIndicatorValueService distributedIndicatorValueService) { + super(distributedIndicatorValueService); this.cachingEnabled = true; } @@ -76,19 +76,19 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator { return Double.NaN; } + // take samples, current value before current time to prevent negative ping times + final double value = this.currentValue; + final long currentTimeMillis = DateTimeUtils.currentTimeMillis(); + if (this.initialized && !this.cachingEnabled && this.active - && this.lastUpdate != this.distributedPingCache.lastUpdate()) { + && this.lastUpdate != this.distributedIndicatorValueService.lastUpdate()) { - final long currentTimeMillis = DateTimeUtils.currentTimeMillis(); this.currentValue = computeValueAt(currentTimeMillis); - this.lastUpdate = this.distributedPingCache.lastUpdate(); - return (currentTimeMillis < this.currentValue) - ? DateTimeUtils.currentTimeMillis() - this.currentValue - : currentTimeMillis - this.currentValue; - - } else { - return DateTimeUtils.currentTimeMillis() - this.currentValue; + this.lastUpdate = this.distributedIndicatorValueService.lastUpdate(); } + + final double res = currentTimeMillis - value; + return res >= 0.0D ? res : 0.0D; } @Override @@ -105,7 +105,7 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator { public final double computeValueAt(final long timestamp) { if (super.ditributedIndicatorValueRecordId != null) { - final Long lastPing = this.distributedPingCache + final Long lastPing = this.distributedIndicatorValueService .getIndicatorValue(super.ditributedIndicatorValueRecordId); return (lastPing != null) @@ -118,22 +118,27 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator { @Override public final boolean hasIncident() { + if (!this.active) { + return false; + } + return getValue() >= super.incidentThreshold; } private double lastCheckVal = 0; - public final boolean missingPingUpdate(final long now) { - if (this.currentValue <= 0) { + public final boolean changeOnIncident() { + if (!this.active || this.currentValue <= 0) { return false; } - final double val = now - this.currentValue; - // check if incidentThreshold was passed (up or down) since last update - final boolean result = (this.lastCheckVal < this.incidentThreshold && val >= this.incidentThreshold) || + final double val = getValue(); + // check if incident threshold has passed (up or down) since last update + final boolean changed = (this.lastCheckVal < this.incidentThreshold && val >= this.incidentThreshold) || (this.lastCheckVal >= this.incidentThreshold && val < this.incidentThreshold); + this.lastCheckVal = val; - return result; + return changed; } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java index 6a09d787..5241d681 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java @@ -40,6 +40,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ExamDeletionEvent; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService; +import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamFinishedEvent; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringRoomService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; @@ -57,6 +59,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService private final RemoteProctoringRoomDAO remoteProctoringRoomDAO; private final ClientConnectionDAO clientConnectionDAO; private final ExamAdminService examAdminService; + private final ProctoringAdminService proctoringAdminService; private final ExamSessionService examSessionService; private final SEBClientInstructionService sebInstructionService; private final boolean sendBroadcastReset; @@ -65,6 +68,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService final RemoteProctoringRoomDAO remoteProctoringRoomDAO, final ClientConnectionDAO clientConnectionDAO, final ExamAdminService examAdminService, + final ProctoringAdminService proctoringAdminService, final ExamSessionService examSessionService, final SEBClientInstructionService sebInstructionService, @Value("${sebserver.webservice.proctoring.resetBroadcastOnLeav:true}") final boolean sendBroadcastReset) { @@ -72,6 +76,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService this.remoteProctoringRoomDAO = remoteProctoringRoomDAO; this.clientConnectionDAO = clientConnectionDAO; this.examAdminService = examAdminService; + this.proctoringAdminService = proctoringAdminService; this.examSessionService = examSessionService; this.sebInstructionService = sebInstructionService; this.sendBroadcastReset = sendBroadcastReset; @@ -145,7 +150,8 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService event.ids.forEach(examId -> { try { - this.examAdminService.examForPK(examId) + this.examAdminService + .examForPK(examId) .flatMap(this::disposeRoomsForExam) .getOrThrow(); @@ -155,6 +161,15 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService }); } + @EventListener + public void notifyExamFinished(final ExamFinishedEvent event) { + + log.info("ExamFinishedEvent received, process disposeRoomsForExam..."); + + disposeRoomsForExam(event.exam) + .onError(error -> log.error("Failed to dispose rooms for finished exam: {}", event.exam, error)); + } + @Override public Result disposeRoomsForExam(final Exam exam) { @@ -166,7 +181,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService .getProctoringServiceSettings(exam.id) .getOrThrow(); - this.examAdminService + this.proctoringAdminService .getExamProctoringService(proctoringSettings.serverType) .flatMap(service -> service.disposeServiceRoomsForExam(exam.id, proctoringSettings)) .onError(error -> log.error("Failed to dispose proctoring service rooms for exam: {} / {}", @@ -194,7 +209,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService .getProctoringServiceSettings(examId) .getOrThrow(); - final ExamProctoringService examProctoringService = this.examAdminService + final ExamProctoringService examProctoringService = this.proctoringAdminService .getExamProctoringService(settings.serverType) .getOrThrow(); @@ -232,7 +247,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService .getProctoringServiceSettings(examId) .getOrThrow(); - final ExamProctoringService examProctoringService = this.examAdminService + final ExamProctoringService examProctoringService = this.proctoringAdminService .getExamProctoringService(settings.serverType) .getOrThrow(); @@ -241,7 +256,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService .flatMap(room -> this.remoteProctoringRoomDAO.createBreakOutRoom(examId, room, connectionTokens)) .getOrThrow(); - return this.examAdminService + return this.proctoringAdminService .getExamProctoringService(settings.serverType) .map(service -> sendJoinRoomBreakOutInstructions( settings, @@ -262,7 +277,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService .getProctoringServiceSettings(examId) .getOrThrow(); - final ExamProctoringService examProctoringService = this.examAdminService + final ExamProctoringService examProctoringService = this.proctoringAdminService .getExamProctoringService(settings.serverType) .getOrThrow(); @@ -347,7 +362,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService .getProctoringServiceSettings(examId) .getOrThrow(); - final ExamProctoringService examProctoringService = this.examAdminService + final ExamProctoringService examProctoringService = this.proctoringAdminService .getExamProctoringService(proctoringSettings.serverType) .getOrThrow(); @@ -391,7 +406,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService .getProctoringServiceSettings(examId) .getOrThrow(); - final ExamProctoringService examProctoringService = this.examAdminService + final ExamProctoringService examProctoringService = this.proctoringAdminService .getExamProctoringService(proctoringSettings.serverType) .getOrThrow(); @@ -564,7 +579,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService .getProctoringServiceSettings(examId) .getOrThrow(); - final ExamProctoringService examProctoringService = this.examAdminService + final ExamProctoringService examProctoringService = this.proctoringAdminService .getExamProctoringService(settings.serverType) .getOrThrow(); @@ -625,7 +640,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService .getProctoringServiceSettings(examId) .getOrThrow(); - final ExamProctoringService examProctoringService = this.examAdminService + final ExamProctoringService examProctoringService = this.proctoringAdminService .getExamProctoringService(settings.serverType) .getOrThrow(); @@ -679,7 +694,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService final String roomName, final String subject) { - final ExamProctoringService examProctoringService = this.examAdminService + final ExamProctoringService examProctoringService = this.proctoringAdminService .getExamProctoringService(proctoringSettings.serverType) .getOrThrow(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java index ab27ce25..34ca5f18 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/JitsiProctoringService.java @@ -60,6 +60,7 @@ import ch.ethz.seb.sebserver.gbl.util.Cryptor; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.webservice.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; @@ -107,19 +108,22 @@ public class JitsiProctoringService implements ExamProctoringService { private final Cryptor cryptor; private final ClientHttpRequestFactoryService clientHttpRequestFactoryService; private final JSONMapper jsonMapper; + private final WebserviceInfo webserviceInfo; protected JitsiProctoringService( final AuthorizationService authorizationService, final ExamSessionService examSessionService, final Cryptor cryptor, final ClientHttpRequestFactoryService clientHttpRequestFactoryService, - final JSONMapper jsonMapper) { + final JSONMapper jsonMapper, + final WebserviceInfo webserviceInfo) { this.authorizationService = authorizationService; this.examSessionService = examSessionService; this.cryptor = cryptor; this.clientHttpRequestFactoryService = clientHttpRequestFactoryService; this.jsonMapper = jsonMapper; + this.webserviceInfo = webserviceInfo; } @Override @@ -141,6 +145,11 @@ public class JitsiProctoringService implements ExamProctoringService { "proctoringSettings:serverURL:invalidURL"); } + // In testing we do not check the JITSI service on URL to be able to test without service + if (this.webserviceInfo != null && this.webserviceInfo.hasProfile("test")) { + return true; + } + final ClientHttpRequestFactory clientHttpRequestFactory = this.clientHttpRequestFactoryService .getClientHttpRequestFactory() .getOrThrow(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java index 3be70848..3227938b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java @@ -455,7 +455,6 @@ public class ZoomProctoringService implements ExamProctoringService { e); } }); - } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceSecurityConfig.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceSecurityConfig.java index 722bdcb9..89accdfa 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceSecurityConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceSecurityConfig.java @@ -101,7 +101,7 @@ public class WebServiceSecurityConfig extends WebSecurityConfigurerAdapter { private Integer adminAccessTokenValSec; @Value("${sebserver.webservice.api.admin.refreshTokenValiditySeconds:-1}") private Integer adminRefreshTokenValSec; - @Value("${sebserver.webservice.api.exam.accessTokenValiditySeconds:3600}") + @Value("${sebserver.webservice.api.exam.accessTokenValiditySeconds:43200}") private Integer examAccessTokenValSec; @Lazy @@ -250,7 +250,7 @@ public class WebServiceSecurityConfig extends WebSecurityConfigurerAdapter { true, 3, adminAccessTokenValSec, - -1); + 1); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java index 19af5f4a..93f9ab1c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java @@ -192,7 +192,8 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler { final APIConstraintViolationException ex, final WebRequest request) { - log.warn("Illegal API Argument Exception: ", ex); + log.warn("Illegal API Argument Exception: {}", ex.getMessage()); + return APIMessage.ErrorMessage.ILLEGAL_API_ARGUMENT .createErrorResponse(ex.getMessage()); } @@ -239,6 +240,17 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler { HttpStatus.BAD_REQUEST); } + @ExceptionHandler(UnsupportedOperationException.class) + public ResponseEntity handleUnsupportedOperationException( + final UnsupportedOperationException ex, + final WebRequest request) { + + return new ResponseEntity<>( + Arrays.asList(APIMessage.ErrorMessage.ILLEGAL_API_ARGUMENT.of(ex)), + Utils.createJsonContentHeader(), + HttpStatus.BAD_REQUEST); + } + @ExceptionHandler(CompletionException.class) public ResponseEntity handleCompletionException( final CompletionException ex, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ActivatableEntityController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ActivatableEntityController.java index ca941abe..80d50421 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ActivatableEntityController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ActivatableEntityController.java @@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestParam; import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Activatable; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityName; import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; @@ -32,13 +33,17 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ActivatableEntityDAO; 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; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Content; /** 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 The concrete Entity domain-model type used on all GET, PUT * @param The concrete Entity domain-model type used for POST methods (new) */ -public abstract class ActivatableEntityController +public abstract class ActivatableEntityController extends EntityController { public ActivatableEntityController( @@ -57,7 +62,29 @@ public abstract class ActivatableEntityController getAll(filterMap)).getOrThrow(); } + @Operation( + summary = "Get a page of all specific domain entity that are currently inactive.", + description = "Sorting: the sort parameter to sort the list of entities before paging\n" + + "the sort parameter is the name of the entity-model attribute to sort with a leading '-' sign for\n" + + "descending sort order. Note that not all entity-model attribute are suited for sorting while the most\n" + + "are.\n", + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }), + parameters = { + @Parameter( + name = Page.ATTR_PAGE_NUMBER, + description = "The number of the page to get from the whole list. If the page does not exists, the API retruns with the first page."), + @Parameter( + name = Page.ATTR_PAGE_SIZE, + description = "The size of the page to get."), + @Parameter( + name = Page.ATTR_SORT, + description = "the sort parameter to sort the list of entities before paging"), + @Parameter( + name = API.PARAM_INSTITUTION_ID, + description = "The institution identifier of the request.\n" + + "Default is the institution identifier of the institution of the current user"), + + }) @RequestMapping( path = API.INACTIVE_PATH_SEGMENT, method = RequestMethod.GET, @@ -114,32 +165,53 @@ public abstract class ActivatableEntityController getAll(filterMap)).getOrThrow(); } + @Operation( + summary = "Activate a single entity by its modelId.", + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }), + parameters = { + @Parameter( + name = API.PARAM_MODEL_ID, + description = "The model identifier of the entity object to activate.", + in = ParameterIn.PATH) + }) @RequestMapping( path = API.PATH_VAR_ACTIVE, method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public EntityProcessingReport activate(@PathVariable final String modelId) { - return setActive(modelId, true) + return setActiveSingle(modelId, true) .getOrThrow(); } + @Operation( + summary = "Dectivate a single entity by its modelId.", + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }), + parameters = { + @Parameter( + name = API.PARAM_MODEL_ID, + description = "The model identifier of the entity object to deactivate.", + in = ParameterIn.PATH) + }) @RequestMapping( value = API.PATH_VAR_INACTIVE, method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public EntityProcessingReport deactivate(@PathVariable final String modelId) { - return setActive(modelId, false) + return setActiveSingle(modelId, false) .getOrThrow(); } - private Result setActive(final String modelId, final boolean active) { + private Result setActiveSingle(final String modelId, final boolean active) { final EntityType entityType = this.entityDAO.entityType(); - return this.entityDAO.byModelId(modelId) + return this.entityDAO + .byModelId(modelId) .flatMap(this.authorization::checkWrite) - .flatMap(this::validForActivation) + .flatMap(entity -> validForActivation(entity, active)) .flatMap(entity -> { final Result createReport = this.bulkActionService.createReport(new BulkAction( @@ -151,8 +223,12 @@ public abstract class ActivatableEntityController validForActivation(final T entity) { - return Result.of(entity); + protected Result validForActivation(final T entity, final boolean activation) { + if ((entity.isActive() && !activation) || (!entity.isActive() && activation)) { + return Result.of(entity); + } else { + throw new IllegalArgumentException("Activation argument mismatch."); + } } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/BatchActionController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/BatchActionController.java new file mode 100644 index 00000000..232e5b01 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/BatchActionController.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.weblayer.api; + +import org.mybatis.dynamic.sql.SqlTable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.POSTMapper; +import ch.ethz.seb.sebserver.gbl.model.BatchAction; +import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.BatchActionRecordDynamicSqlSupport; +import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BatchActionService; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; + +@WebServiceProfile +@RestController +@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.BATCH_ACTION_ENDPOINT) +public class BatchActionController extends EntityController { + + private final BatchActionService batchActionService; + + protected BatchActionController( + final AuthorizationService authorization, + final BulkActionService bulkActionService, + final EntityDAO entityDAO, + final UserActivityLogDAO userActivityLogDAO, + final PaginationService paginationService, + final BeanValidationService beanValidationService, + final BatchActionService batchActionService) { + + super( + authorization, + bulkActionService, + entityDAO, + userActivityLogDAO, + paginationService, + beanValidationService); + + this.batchActionService = batchActionService; + } + + @Override + protected BatchAction createNew(final POSTMapper postParams) { + return new BatchAction( + null, + super.authorization.getUserService().getCurrentUser().getUserInfo().uuid, + postParams); + } + + @Override + protected Result validForCreate(final BatchAction entity) { + return this.batchActionService.validate(entity); + } + + @Override + protected Result validForSave(final BatchAction entity) { + throw new UnsupportedOperationException("Save already existing BatchAction is not supported"); + } + + @Override + protected Result notifyCreated(final BatchAction entity) { + return this.batchActionService.notifyNewBatchAction(entity); + } + + @Override + protected SqlTable getSQLTableOfEntity() { + return BatchActionRecordDynamicSqlSupport.batchActionRecord; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/CertificateController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/CertificateController.java index 41ec58cf..d03ca5c2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/CertificateController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/CertificateController.java @@ -53,10 +53,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; 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.sebconfig.CertificateService; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; @WebServiceProfile @RestController @RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.CERTIFICATE_ENDPOINT) +@SecurityRequirement(name = "oauth2") public class CertificateController { private final AuthorizationService authorization; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java index 3362518c..8973f4cd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java @@ -8,30 +8,52 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; +import java.util.Arrays; import java.util.Collection; +import java.util.Comparator; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.mybatis.dynamic.sql.SqlTable; +import org.springframework.http.MediaType; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.EntityDependency; +import ch.ethz.seb.sebserver.gbl.model.Page; +import ch.ethz.seb.sebserver.gbl.model.PageSortOrder; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; +import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; import ch.ethz.seb.sebserver.gbl.model.user.UserRole; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO; 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.session.SEBClientConnectionService; import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; @WebServiceProfile @@ -39,13 +61,18 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe @RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.SEB_CLIENT_CONNECTION_ENDPOINT) public class ClientConnectionController extends ReadonlyEntityController { + private final SEBClientConnectionService sebClientConnectionService; + + private static final Set EXT_FILTER = new HashSet<>(Arrays.asList(ClientConnection.FILTER_ATTR_INFO)); + protected ClientConnectionController( final AuthorizationService authorization, final BulkActionService bulkActionService, final ClientConnectionDAO clientConnectionDAO, final UserActivityLogDAO userActivityLogDAO, final PaginationService paginationService, - final BeanValidationService beanValidationService) { + final BeanValidationService beanValidationService, + final SEBClientConnectionService sebClientConnectionService) { super(authorization, bulkActionService, @@ -53,18 +80,103 @@ public class ClientConnectionController extends ReadonlyEntityController> getAll(final FilterMap filterMap) { - final String infoFilter = filterMap.getString(ClientConnection.FILTER_ATTR_INFO); - if (StringUtils.isNotBlank(infoFilter)) { - return super.getAll(filterMap) - .map(all -> all.stream().filter(c -> c.getInfo() == null || c.getInfo().contains(infoFilter)) - .collect(Collectors.toList())); - } + @RequestMapping( + method = RequestMethod.GET, + consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + public Page getPage( + @RequestParam( + name = API.PARAM_INSTITUTION_ID, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, + @RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber, + @RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize, + @RequestParam(name = Page.ATTR_SORT, required = false) final String sort, + @RequestParam final MultiValueMap allRequestParams, + final HttpServletRequest request) { - return super.getAll(filterMap); + // at least current user must have read access for specified entity type within its own institution + checkReadPrivilege(institutionId); + + final FilterMap filterMap = new FilterMap(allRequestParams, request.getQueryString()); + populateFilterMap(filterMap, institutionId, sort); + + if (StringUtils.isNotBlank(sort) || filterMap.containsAny(EXT_FILTER)) { + + final Collection allConnections = getAll(filterMap) + .getOrThrow(); + + return this.paginationService.buildPageFromList( + pageNumber, + pageSize, + sort, + allConnections, + pageClientConnectionFunction(filterMap, sort)); + + } else { + return super.getPage(institutionId, pageNumber, pageSize, sort, allRequestParams, request); + } + } + + @RequestMapping( + path = API.SEB_CLIENT_CONNECTION_DATA_ENDPOINT, + method = RequestMethod.GET, + consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + public Page getClientConnectionDataPage( + @RequestParam( + name = API.PARAM_INSTITUTION_ID, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, + @RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber, + @RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize, + @RequestParam(name = Page.ATTR_SORT, required = false) final String sort, + @RequestParam final MultiValueMap allRequestParams, + final HttpServletRequest request) { + + // at least current user must have read access for specified entity type within its own institution + checkReadPrivilege(institutionId); + + final FilterMap filterMap = new FilterMap(allRequestParams, request.getQueryString()); + populateFilterMap(filterMap, institutionId, sort); + + if (StringUtils.isNotBlank(sort) || filterMap.containsAny(EXT_FILTER)) { + + final Collection allConnections = getAllData(filterMap) + .getOrThrow(); + + return this.paginationService.buildPageFromList( + pageNumber, + pageSize, + sort, + allConnections, + pageClientConnectionDataFunction(filterMap, sort)); + } else { + + return this.paginationService.getPage( + pageNumber, + pageSize, + sort, + getSQLTableOfEntity().name(), + () -> getAllData(filterMap)) + .getOrThrow(); + } + } + + @RequestMapping( + path = API.SEB_CLIENT_CONNECTION_DATA_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT, + method = RequestMethod.GET, + consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + public ClientConnectionData getClientConnectionDataBy(@PathVariable final String modelId) { + return this.sebClientConnectionService + .getIndicatorValues(super.getBy(modelId)) + .getOrThrow(); } @Override @@ -106,4 +218,116 @@ public class ClientConnectionController extends ReadonlyEntityController> getAllData(final FilterMap filterMap) { + return getAll(filterMap) + .map(connections -> connections.stream() + .map(this.sebClientConnectionService::getIndicatorValues) + .flatMap(Result::onErrorLogAndSkip) + .collect(Collectors.toList())); + } + + private Function, List> pageClientConnectionFunction( + final FilterMap filterMap, + final String sort) { + + return connections -> { + + final List filtered = connections + .stream() + .filter(getClientConnectionFilter(filterMap)) + .collect(Collectors.toList()); + + if (StringUtils.isNotBlank(sort)) { + filtered.sort(new ClientConnectionComparator(sort)); + } + return filtered; + }; + } + + private Function, List> pageClientConnectionDataFunction( + final FilterMap filterMap, + final String sort) { + + return connections -> { + + final List filtered = connections + .stream() + .filter(getClientConnectionDataFilter(filterMap)) + .collect(Collectors.toList()); + + if (StringUtils.isNotBlank(sort)) { + filtered.sort(new ClientConnectionDataComparator(sort)); + } + return filtered; + }; + } + + private Predicate getClientConnectionFilter(final FilterMap filterMap) { + final String infoFilter = filterMap.getString(ClientConnection.FILTER_ATTR_INFO); + Predicate filter = Utils.truePredicate(); + if (StringUtils.isNotBlank(infoFilter)) { + filter = c -> c.getInfo() == null || c.getInfo().contains(infoFilter); + } + return filter; + } + + private Predicate getClientConnectionDataFilter(final FilterMap filterMap) { + final Predicate clientConnectionFilter = getClientConnectionFilter(filterMap); + return ccd -> clientConnectionFilter.test(ccd.clientConnection); + } + + private static final class ClientConnectionComparator implements Comparator { + + final String sortColumn; + final boolean descending; + + ClientConnectionComparator(final String sort) { + this.sortColumn = PageSortOrder.decode(sort); + this.descending = PageSortOrder.getSortOrder(sort) == PageSortOrder.DESCENDING; + } + + @Override + public int compare(final ClientConnection cc1, final ClientConnection cc2) { + int result = 0; + if (Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID.equals(this.sortColumn)) { + result = ObjectUtils.compare(cc1.userSessionId, cc2.userSessionId); + } else if (ClientConnection.ATTR_INFO.equals(this.sortColumn)) { + result = ObjectUtils.compare(cc1.getInfo(), cc2.getInfo()); + } else if (Domain.CLIENT_CONNECTION.ATTR_STATUS.equals(this.sortColumn)) { + result = ObjectUtils.compare(cc1.getStatus(), cc2.getStatus()); + } else { + result = ObjectUtils.compare(cc1.userSessionId, cc2.userSessionId); + } + return (this.descending) ? -result : result; + } + } + + private static final class ClientConnectionDataComparator implements Comparator { + + final ClientConnectionComparator clientConnectionComparator; + + ClientConnectionDataComparator(final String sort) { + this.clientConnectionComparator = new ClientConnectionComparator(sort); + } + + @Override + public int compare(final ClientConnectionData cc1, final ClientConnectionData cc2) { + if (this.clientConnectionComparator.sortColumn.startsWith(ClientConnectionData.ATTR_INDICATOR_VALUE)) { + try { + final Long iValuePK = Long.valueOf(StringUtils.split( + this.clientConnectionComparator.sortColumn, + Constants.UNDERLINE)[1]); + final Double indicatorValue1 = cc1.getIndicatorValue(iValuePK); + final Double indicatorValue2 = cc2.getIndicatorValue(iValuePK); + final int result = indicatorValue1.compareTo(indicatorValue2); + return (this.clientConnectionComparator.descending) ? -result : result; + } catch (final Exception e) { + this.clientConnectionComparator.compare(cc1.clientConnection, cc2.clientConnection); + } + } + + return this.clientConnectionComparator.compare(cc1.clientConnection, cc2.clientConnection); + } + } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationNodeController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationNodeController.java index 9468efda..85f8c5d1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationNodeController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationNodeController.java @@ -72,6 +72,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ViewDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ExamConfigService; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ExamConfigTemplateService; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamConfigUpdateService; import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; @WebServiceProfile @@ -87,6 +88,7 @@ public class ConfigurationNodeController extends EntityController { + this.examConfigUpdateService + .processExamConfigurationChange(node.id) + .getOrThrow(); + return node; + }) + .getOrThrow(); + } + @RequestMapping( path = API.CONFIGURATION_COPY_PATH_SEGMENT, method = RequestMethod.PUT, @@ -183,7 +207,8 @@ public class ConfigurationNodeController extends EntityController validForSave(final ConfigurationNode entity) { - return super.validForSave(entity) - .map(e -> { - final ConfigurationNode existingNode = this.entityDAO.byPK(entity.id) - .getOrThrow(); - if (existingNode.type != entity.type) { - throw new APIConstraintViolationException( - "The Type of ConfigurationNode cannot change after creation"); - } - return e; - }) - .map(this::checkChangeToArchived); - } - - private ConfigurationNode checkChangeToArchived(final ConfigurationNode entity) { - if (entity.status == ConfigurationStatus.ARCHIVED) { - // check if we have a change to archived - final ConfigurationNode persistentNode = this.configurationNodeDAO - .byPK(entity.id) - .getOrThrow(); - // yes we have - if (persistentNode.status != ConfigurationStatus.ARCHIVED) { - // check if this is possible (no upcoming or running exams involved) - if (!this.examConfigurationMapDAO.checkNoActiveExamReferences(entity.id).getOr(false)) { - throw new APIMessageException( - APIMessage.ErrorMessage.INTEGRITY_VALIDATION - .of("Exam configuration has references to at least one upcoming or running exam.")); - } - } - } - - return entity; + return this.sebExamConfigService.checkSaveConsistency(entity); } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java index 3f8359c1..fbf9d380 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java @@ -56,12 +56,18 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO; 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; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; /** Abstract Entity-Controller that defines generic Entity rest API endpoints that are supported * by all entity types. * * @param The concrete Entity domain-model type used on all GET, PUT * @param The concrete Entity domain-model type used for POST methods (new) */ +@SecurityRequirement(name = "oauth2") public abstract class EntityController { private static final Logger log = LoggerFactory.getLogger(EntityController.class); @@ -133,6 +139,41 @@ public abstract class EntityController { * 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 */ + @Operation( + summary = "Get a page of the specific domain entity. Sorting and filtering is applied before paging", + description = "Sorting: the sort parameter to sort the list of entities before paging\n" + + "the sort parameter is the name of the entity-model attribute to sort with a leading '-' sign for\n" + + "descending sort order. Note that not all entity-model attribute are suited for sorting while the most\n" + + "are.\n" + + "

\n" + + "Filter: The filter attributes accepted by this API depend on the actual entity model (domain object)\n" + + "and are of the form [domain-attribute-name]=[filter-value]. E.g.: name=abc or type=EXAM. Usually\n" + + "filter attributes of text type are treated as SQL wildcard with %[text]% to filter all text containing\n" + + "a given text-snippet.", + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }), + parameters = { + @Parameter( + name = Page.ATTR_PAGE_NUMBER, + description = "The number of the page to get from the whole list. If the page does not exists, the API retruns with the first page."), + @Parameter( + name = Page.ATTR_PAGE_SIZE, + description = "The size of the page to get."), + @Parameter( + name = Page.ATTR_SORT, + description = "the sort parameter to sort the list of entities before paging"), + @Parameter( + name = API.PARAM_INSTITUTION_ID, + description = "The institution identifier of the request.\n" + + "Default is the institution identifier of the institution of the current user"), + @Parameter( + name = "filterCriteria", + description = "Additional filter criterias \n" + + "For OpenAPI 3 input please use the form: {\"columnName\":\"filterValue\"}", + example = "{\"name\":\"ethz\"}", + required = false, + allowEmptyValue = true) + }) @RequestMapping( method = RequestMethod.GET, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, @@ -165,23 +206,33 @@ public abstract class EntityController { return page; } - protected void populateFilterMap(final FilterMap filterMap, final Long institutionId, final String sort) { - // If current user has no read access for specified entity type within other institution - // then the current users institutionId is put as a SQL filter criteria attribute to extends query performance - if (!this.authorization.hasGrant(PrivilegeType.READ, getGrantEntityType())) { - filterMap.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId)); - } - - // If sorting is on institution name we need to join the institution table - if (sort != null && sort.contains(Entity.FILTER_ATTR_INSTITUTION)) { - filterMap.putIfAbsent(FilterMap.ATTR_ADD_INSITUTION_JOIN, Constants.TRUE_STRING); - } - } - // ****************** // * GET (names) // ****************** + @Operation( + summary = "Get a filtered list of specific entity name keys.", + description = "An entity name key is a minimal entity data object with the entity-type, modelId and the name of the entity." + + "

\n" + + "Filter: The filter attributes accepted by this API depend on the actual entity model (domain object)\n" + + "and are of the form [domain-attribute-name]=[filter-value]. E.g.: name=abc or type=EXAM. Usually\n" + + "filter attributes of text type are treated as SQL wildcard with %[text]% to filter all text containing\n" + + "a given text-snippet.", + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }), + parameters = { + @Parameter( + name = API.PARAM_INSTITUTION_ID, + description = "The institution identifier of the request.\n" + + "Default is the institution identifier of the institution of the current user"), + @Parameter( + name = "filterCriteria", + description = "Additional filter criterias \n" + + "For OpenAPI 3 input please use the form: {\"columnName\":\"filterValue\"}", + example = "{\"name\":\"ethz\"}", + required = false, + allowEmptyValue = true) + }) @RequestMapping( path = API.NAMES_PATH_SEGMENT, method = RequestMethod.GET, @@ -219,6 +270,34 @@ public abstract class EntityController { // * GET (dependency) // ****************** + @Operation( + summary = "Get a list of dependency keys of all dependent entity objects for a " + + "specified source entity and bulk action.", + description = "Get a list of dependency keys of all dependent entity objects for a " + + "specified source entity and bulk action.\n " + + "This can be used to verify depended objects for a certain bulk action to " + + "give a report of affected objects beforehand.\n " + + "For example for a delete action of a certain object, this gives all objects " + + "that will also be deleted within the deletion of the source object", + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }), + + parameters = { + @Parameter( + name = API.PARAM_MODEL_ID, + description = "The model identifier of the source entity object to geht the dependencies for.", + in = ParameterIn.PATH), + @Parameter( + name = API.PARAM_BULK_ACTION_TYPE, + description = "The bulk action type defining the type of action to get the dependencies for.\n" + + "This is the name of the enumeration "), + @Parameter( + name = API.PARAM_BULK_ACTION_ADD_INCLUDES, + description = "Indicates if the following 'includes' paramerer shall be processed or not.\n The default is false "), + @Parameter( + name = API.PARAM_BULK_ACTION_INCLUDES, + description = "A comma separated list of names of the EntityType enummeration that defines all entity types that shall be included in the result.") + }) @RequestMapping( path = API.MODEL_ID_VAR_PATH_SEGMENT + API.DEPENDENCY_PATH_SEGMENT, method = RequestMethod.GET, @@ -248,6 +327,16 @@ public abstract class EntityController { // * GET (single) // ****************** + @Operation( + summary = "Get a single entity by its modelId.", + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }), + parameters = { + @Parameter( + name = API.PARAM_MODEL_ID, + description = "The model identifier of the entity object to get.", + in = ParameterIn.PATH) + }) @RequestMapping( path = API.MODEL_ID_VAR_PATH_SEGMENT, method = RequestMethod.GET, @@ -265,6 +354,15 @@ public abstract class EntityController { // * GET (list) // ****************** + @Operation( + summary = "Get a list of entity objects by a given list of model identifiers of entities.", + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }), + parameters = { + @Parameter( + name = API.PARAM_MODEL_ID_LIST, + description = "Comma separated list of model identifiers.") + }) @RequestMapping( path = API.LIST_PATH_SEGMENT, method = RequestMethod.GET, @@ -286,6 +384,27 @@ public abstract class EntityController { // * POST (create) // ****************** + @Operation( + summary = "Create a new entity object of specifies type by using the given form parameter", + description = "This expects " + MediaType.APPLICATION_FORM_URLENCODED_VALUE + + " format for the form parameter" + + " and tries to create a new entity object from this form parameter, " + + "resulting in an error if there are missing" + + " or incorrect form paramter. The needed form paramter " + + + "can be verified within the specific entity object.", + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }), + parameters = { + @Parameter( + name = "formParams", + description = "The from paramter value map that is been used to create a new entity object.", + in = ParameterIn.DEFAULT), + @Parameter( + name = API.PARAM_INSTITUTION_ID, + description = "The institution identifier of the request.\n" + + "Default is the institution identifier of the institution of the current user"), + }) @RequestMapping( method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, @@ -318,6 +437,14 @@ public abstract class EntityController { // * PUT (save) // **************** + @Operation( + summary = "Modifies an already existing entity object of the specific type.", + description = "This expects " + MediaType.APPLICATION_JSON_VALUE + + " format for the response data and verifies consistencies " + + "within the definition of the specific entity object type. " + + "Missing (NULL) parameter that are not mandatory will be ignored and the original value will not be affected", + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = { @Content(mediaType = MediaType.APPLICATION_JSON_VALUE) })) @RequestMapping( method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE, @@ -335,6 +462,25 @@ public abstract class EntityController { // * DELETE (hard-delete) // ************************ + @Operation( + summary = "Deletes a single entity (and all its dependencies) by its modelId.", + description = "To check or report what dependent object also would be deleted for a certain entity object, " + + + "please use the dependency endpoint to get a report of all dependend entity objects.", + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }), + parameters = { + @Parameter( + name = API.PARAM_MODEL_ID, + description = "The model identifier of the entity object to get.", + in = ParameterIn.PATH), + @Parameter( + name = API.PARAM_BULK_ACTION_ADD_INCLUDES, + description = "Indicates if the following 'includes' paramerer shall be processed or not.\n The default is false "), + @Parameter( + name = API.PARAM_BULK_ACTION_INCLUDES, + description = "A comma separated list of names of the EntityType enummeration that defines all entity types that shall be included in the result.") + }) @RequestMapping( path = API.MODEL_ID_VAR_PATH_SEGMENT, method = RequestMethod.DELETE, @@ -358,6 +504,25 @@ public abstract class EntityController { // * DELETE ALL (hard-delete) // ************************** + @Operation( + summary = "Deletes all given entity (and all its dependencies) by a given list of model identifiers.", + description = "To check or report what dependent object also would be deleted for a certain entity object, " + + + "please use the dependency endpoint to get a report of all dependend entity objects.", + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }), + parameters = { + @Parameter( + name = API.PARAM_MODEL_ID_LIST, + description = "The list of model identifiers of specific entity type to delete.", + in = ParameterIn.QUERY), + @Parameter( + name = API.PARAM_BULK_ACTION_ADD_INCLUDES, + description = "Indicates if the following 'includes' paramerer shall be processed or not.\n The default is false "), + @Parameter( + name = API.PARAM_BULK_ACTION_INCLUDES, + description = "A comma separated list of names of the EntityType enummeration that defines all entity types that shall be included in the result.") + }) @RequestMapping( method = RequestMethod.DELETE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, @@ -403,6 +568,19 @@ public abstract class EntityController { .getOrThrow(); } + protected void populateFilterMap(final FilterMap filterMap, final Long institutionId, final String sort) { + // If current user has no read access for specified entity type within other institution + // then the current users institutionId is put as a SQL filter criteria attribute to extends query performance + if (!this.authorization.hasGrant(PrivilegeType.READ, getGrantEntityType())) { + filterMap.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId)); + } + + // If sorting is on institution name we need to join the institution table + if (sort != null && sort.contains(Entity.FILTER_ATTR_INSTITUTION)) { + filterMap.putIfAbsent(FilterMap.ATTR_ADD_INSITUTION_JOIN, Constants.TRUE_STRING); + } + } + protected EnumSet convertToEntityType(final boolean addIncludes, final List includes) { final EnumSet includeDependencies = (includes != null) ? (includes.isEmpty()) @@ -605,24 +783,6 @@ public abstract class EntityController { return this.userActivityLogDAO.logModify(entity); } - /** Makes a DELETE user activity log for the specified entity. - * This may be overwritten if the create user activity log should be skipped. - * - * @param entity the Entity instance - * @return Result refer to the logged Entity instance or to an error if happened */ - protected String logDelete(final String modelId) { - try { - return this.entityDAO - .byModelId(modelId) - .flatMap(this::logDelete) - .map(Entity::getModelId) - .getOrThrow(); - } catch (final Exception e) { - log.warn("Failed to log delete for entity id: {}", modelId, e); - return modelId; - } - } - /** Makes a DELETE user activity log for the specified entity. * This may be overwritten if the create user activity log should be skipped. * diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java index 04918acb..8e6aa53f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java @@ -137,6 +137,7 @@ public class ExamAPI_V1_Controller { .getOrThrow() .stream() .map(this::createRunningExamInfo) + .filter(this::checkConsistency) .collect(Collectors.toList()); } else { final Exam exam = this.examSessionService.getExamDAO() @@ -158,6 +159,18 @@ public class ExamAPI_V1_Controller { this.executor); } + private boolean checkConsistency(final RunningExamInfo info) { + if (StringUtils.isNotBlank(info.name) && + StringUtils.isNotBlank(info.url) && + StringUtils.isNotBlank(info.examId)) { + + return true; + } + + log.warn("Invalid running exam detected. Filter out exam : {}", info); + return false; + } + @RequestMapping( path = API.EXAM_API_HANDSHAKE_ENDPOINT, method = RequestMethod.PATCH, @@ -286,23 +299,23 @@ public class ExamAPI_V1_Controller { public void ping(final HttpServletRequest request, final HttpServletResponse response) { final String connectionToken = request.getHeader(API.EXAM_API_SEB_CONNECTION_TOKEN); - final String timeStampString = request.getParameter(API.EXAM_API_PING_TIMESTAMP); + //final String timeStampString = request.getParameter(API.EXAM_API_PING_TIMESTAMP); final String pingNumString = request.getParameter(API.EXAM_API_PING_NUMBER); final String instructionConfirm = request.getParameter(API.EXAM_API_PING_INSTRUCTION_CONFIRM); - long pingTime; - try { - pingTime = Long.parseLong(timeStampString); - } catch (final Exception e) { - log.error("Invalid ping request: {}", connectionToken); - response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); - return; - } +// long pingTime; +// try { +// pingTime = Long.parseLong(timeStampString); +// } catch (final Exception e) { +// log.error("Invalid ping request: {}", connectionToken); +// response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); +// return; +// } final String instruction = this.sebClientConnectionService .notifyPing( connectionToken, - pingTime, + Utils.getMillisecondsNow(), pingNumString != null ? Integer.parseInt(pingNumString) : -1, instructionConfirm); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java index 457f6b78..fb292d8e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java @@ -17,13 +17,12 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; -import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; import org.mybatis.dynamic.sql.SqlTable; import org.springframework.http.MediaType; -import org.springframework.util.MultiValueMap; import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @@ -39,14 +38,13 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage; import ch.ethz.seb.sebserver.gbl.api.EntityType; 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.EntityKey; -import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.PageSortOrder; import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; @@ -120,54 +118,56 @@ public class ExamAdministrationController extends EntityController { return ExamRecordDynamicSqlSupport.examRecord; } - @RequestMapping( - method = RequestMethod.GET, - consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, - produces = MediaType.APPLICATION_JSON_VALUE) - @Override - public Page getPage( - @RequestParam( - name = API.PARAM_INSTITUTION_ID, - required = true, - defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, - @RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber, - @RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize, - @RequestParam(name = Page.ATTR_SORT, required = false) final String sort, - @RequestParam final MultiValueMap allRequestParams, - final HttpServletRequest request) { - - checkReadPrivilege(institutionId); - - // NOTE: several attributes for sorting may be originated by the QuizData from LMS not by the database - // of the SEB Server. Therefore in the case we have no or the default sorting we can use the - // native PaginationService within MyBatis and SQL. For the other cases we need an in-line sorting and paging - if (StringUtils.isBlank(sort) || - (this.paginationService.isNativeSortingSupported(ExamRecordDynamicSqlSupport.examRecord, sort))) { - - return super.getPage(institutionId, pageNumber, pageSize, sort, allRequestParams, request); - - } else { - - this.authorization.check( - PrivilegeType.READ, - EntityType.EXAM, - institutionId); - - final Collection exams = this.examDAO - .allMatching(new FilterMap( - allRequestParams, - request.getQueryString()), - this::hasReadAccess) - .getOrThrow(); - - return this.paginationService.buildPageFromList( - pageNumber, - pageSize, - sort, - exams, - pageSort(sort)); - } - } +// @RequestMapping( +// method = RequestMethod.GET, +// consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, +// produces = MediaType.APPLICATION_JSON_VALUE) +// @Override +// public Page getPage( +// @RequestParam( +// name = API.PARAM_INSTITUTION_ID, +// required = true, +// defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, +// @RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber, +// @RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize, +// @RequestParam(name = Page.ATTR_SORT, required = false) final String sort, +// @RequestParam final MultiValueMap allRequestParams, +// final HttpServletRequest request) { +// +// checkReadPrivilege(institutionId); +// this.authorization.check( +// PrivilegeType.READ, +// EntityType.EXAM, +// institutionId); +// +// if (StringUtils.isBlank(sort) || +// (this.paginationService.isNativeSortingSupported(ExamRecordDynamicSqlSupport.examRecord, sort))) { +// +// System.out.println("*********************** sort, filter on DB"); +// +// return super.getPage(institutionId, pageNumber, pageSize, sort, allRequestParams, request); +// +// } else { +// +// System.out.println("*********************** sort, filter on List"); +// +// return super.getPage(institutionId, pageNumber, pageSize, sort, allRequestParams, request); +// +//// final Collection exams = this.examDAO +//// .allMatching(new FilterMap( +//// allRequestParams, +//// request.getQueryString()), +//// this::hasReadAccess) +//// .getOrThrow(); +//// +//// return this.paginationService.buildPageFromList( +//// pageNumber, +//// pageSize, +//// sort, +//// exams, +//// pageSort(sort)); +// } +// } @RequestMapping( path = API.MODEL_ID_VAR_PATH_SEGMENT @@ -213,6 +213,28 @@ public class ExamAdministrationController extends EntityController { return result; } + @RequestMapping( + path = API.MODEL_ID_VAR_PATH_SEGMENT + + API.EXAM_ADMINISTRATION_ARCHIVE_PATH_SEGMENT, + method = RequestMethod.PATCH, + produces = MediaType.APPLICATION_JSON_VALUE) + public Exam archive( + @PathVariable final Long modelId, + @RequestParam( + name = API.PARAM_INSTITUTION_ID, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId) { + + checkWritePrivilege(institutionId); + return this.examDAO.byPK(modelId) + .flatMap(this::checkWriteAccess) + .flatMap(this.examAdminService::archiveExam) +// .flatMap(this::checkArchive) +// .flatMap(exam -> this.examDAO.updateState(exam.id, ExamStatus.ARCHIVED, null)) + .flatMap(this::logModify) + .getOrThrow(); + } + // **************************************************************************** // **** SEB Restriction @@ -381,12 +403,9 @@ public class ExamAdministrationController extends EntityController { .byPK(examId) .flatMap(this.authorization::checkModify) .map(exam -> { - if (StringUtils.isNotBlank(proctoringServiceSettings.serverURL)) { - this.examAdminService.getExamProctoringService(proctoringServiceSettings.serverType) - .flatMap(service -> service.testExamProctoring(proctoringServiceSettings)) - .getOrThrow(); - } - this.examAdminService.saveProctoringServiceSettings(examId, proctoringServiceSettings); + this.examAdminService + .saveProctoringServiceSettings(examId, proctoringServiceSettings) + .getOrThrow(); return exam; }) .flatMap(this.userActivityLogDAO::logModify) @@ -549,7 +568,6 @@ public class ExamAdministrationController extends EntityController { .of("Exam currently has active SEB Client connections.")); } - // TODO double check before setSEBRestriction return this.checkNoActiveSEBClientConnections(exam) .flatMap(this.sebRestrictionService::applySEBClientRestriction) .flatMap(e -> this.examDAO.setSEBRestriction(exam.id, restrict)) @@ -572,13 +590,13 @@ public class ExamAdministrationController extends EntityController { } if (sortBy.equals(Exam.FILTER_ATTR_NAME) || sortBy.equals(QuizData.QUIZ_ATTR_NAME)) { - list.sort(Comparator.comparing(exam -> exam.name)); + list.sort(Comparator.comparing(exam -> (exam.name != null) ? exam.name : StringUtils.EMPTY)); } if (sortBy.equals(Exam.FILTER_ATTR_TYPE)) { - list.sort(Comparator.comparing(exam -> exam.type)); + list.sort(Comparator.comparing(exam -> (exam.type != null) ? exam.type : ExamType.UNDEFINED)); } if (sortBy.equals(QuizData.FILTER_ATTR_START_TIME) || sortBy.equals(QuizData.QUIZ_ATTR_START_TIME)) { - list.sort(Comparator.comparing(exam -> exam.startTime)); + list.sort(Comparator.comparing(exam -> (exam.startTime != null) ? exam.startTime : new DateTime(0))); } if (PageSortOrder.DESCENDING == PageSortOrder.getSortOrder(sort)) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java index 69400d22..66f1aa09 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java @@ -190,7 +190,9 @@ public class ExamConfigurationMappingController extends EntityController entity); } @@ -207,7 +209,9 @@ public class ExamConfigurationMappingController extends EntityController pair); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java index 0c1639b2..69f81a65 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java @@ -63,10 +63,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientConnectionService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientInstructionService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientNotificationService; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; @WebServiceProfile @RestController @RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.EXAM_MONITORING_ENDPOINT) +@SecurityRequirement(name = "oauth2") public class ExamMonitoringController { private static final Logger log = LoggerFactory.getLogger(ExamMonitoringController.class); @@ -174,6 +176,67 @@ public class ExamMonitoringController { ExamAdministrationController.pageSort(sort)); } + /** Get a page of all currently finished exams + * + * GET /{api}/{entity-type-endpoint-name} + * + * GET /admin-api/v1/monitoring + * GET /admin-api/v1/monitoring?page_number=2&page_size=10&sort=-name + * GET /admin-api/v1/monitoring?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( + path = API.EXAM_MONITORING_FINISHED_ENDPOINT, + method = RequestMethod.GET, + consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + public Page getFinishedExamsPage( + @RequestParam( + name = API.PARAM_INSTITUTION_ID, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, + @RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber, + @RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize, + @RequestParam(name = Page.ATTR_SORT, required = false) final String sort, + @RequestParam final MultiValueMap allRequestParams, + final HttpServletRequest request) { + + this.authorization.checkRole( + institutionId, + EntityType.EXAM, + UserRole.EXAM_SUPPORTER, + UserRole.EXAM_ADMIN); + + final FilterMap filterMap = new FilterMap(allRequestParams, request.getQueryString()); + + // if current user has no read access for specified entity type within other institution + // then the current users institutionId is put as a SQL filter criteria attribute to extends query performance + if (!this.authorization.hasGrant(PrivilegeType.READ, EntityType.EXAM)) { + filterMap.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId)); + } + + final Collection exams = this.examSessionService + .getFilteredFinishedExams( + filterMap, + exam -> this.hasRunningExamPrivilege(exam, institutionId)) + .getOrThrow(); + + return this.paginationService.buildPageFromList( + pageNumber, + pageSize, + sort, + exams, + ExamAdministrationController.pageSort(sort)); + } + @RequestMapping( path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT, method = RequestMethod.GET, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamProctoringController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamProctoringController.java index 10a0efb1..680db670 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamProctoringController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamProctoringController.java @@ -37,10 +37,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringRoomService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; @WebServiceProfile @RestController @RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.EXAM_PROCTORING_ENDPOINT) +@SecurityRequirement(name = "oauth2") public class ExamProctoringController { private static final Logger log = LoggerFactory.getLogger(ExamProctoringController.class); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamTemplateController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamTemplateController.java index 4c223a93..3e9b237d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamTemplateController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamTemplateController.java @@ -39,6 +39,7 @@ import ch.ethz.seb.sebserver.gbl.model.PageSortOrder; import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.IndicatorTemplate; +import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; @@ -48,6 +49,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionServic import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamTemplateDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService; +import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService; import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; @WebServiceProfile @@ -56,6 +59,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe public class ExamTemplateController extends EntityController { private final ExamTemplateDAO examTemplateDAO; + private final ProctoringAdminService proctoringServiceSettingsService; protected ExamTemplateController( final AuthorizationService authorization, @@ -63,7 +67,8 @@ public class ExamTemplateController extends EntityController { + ExamAdminService.checkThresholdConsistency(indicator.thresholds); + return indicator; + }) .flatMap(this.examTemplateDAO::createNewIndicatorTemplate) .flatMap(this.userActivityLogDAO::logCreate) .getOrThrow(); @@ -204,6 +217,10 @@ public class ExamTemplateController extends EntityController { + ExamAdminService.checkThresholdConsistency(indicator.thresholds); + return indicator; + }) .flatMap(this.examTemplateDAO::saveIndicatorTemplate) .flatMap(this.userActivityLogDAO::logModify) .getOrThrow(); @@ -225,11 +242,66 @@ public class ExamTemplateController extends EntityController { + this.proctoringServiceSettingsService.saveProctoringServiceSettings( + new EntityKey(examId, EntityType.EXAM_TEMPLATE), + proctoringServiceSettings) + .getOrThrow(); + return examTemplate; + }) + .flatMap(this.userActivityLogDAO::logModify) + .getOrThrow(); + } + + // **** Proctoring + // **************************************************************************** + @Override protected ExamTemplate createNew(final POSTMapper postParams) { final Long institutionId = postParams.getLong(API.PARAM_INSTITUTION_ID); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IndicatorController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IndicatorController.java index c5e4d833..387a7f3d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IndicatorController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IndicatorController.java @@ -8,25 +8,17 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - import org.mybatis.dynamic.sql.SqlTable; -import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import ch.ethz.seb.sebserver.gbl.api.API; -import ch.ethz.seb.sebserver.gbl.api.APIMessage; -import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; import ch.ethz.seb.sebserver.gbl.model.GrantEntity; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; -import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Pair; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -37,6 +29,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionServic import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.IndicatorDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; @@ -95,22 +88,20 @@ public class IndicatorController extends EntityController @Override protected Result validForCreate(final Indicator entity) { - final Result validForCreate = super.validForCreate(entity); - if (validForCreate.hasError()) { - return validForCreate; - } - - return checkThresholdConsistency(entity); + return super.validForCreate(entity) + .map(indicator -> { + ExamAdminService.checkThresholdConsistency(indicator.thresholds); + return indicator; + }); } @Override protected Result validForSave(final Indicator entity) { - final Result validForSave = super.validForSave(entity); - if (validForSave.hasError()) { - return validForSave; - } - - return checkThresholdConsistency(entity); + return super.validForSave(entity) + .map(indicator -> { + ExamAdminService.checkThresholdConsistency(indicator.thresholds); + return indicator; + }); } @Override @@ -156,33 +147,4 @@ public class IndicatorController extends EntityController } - private Result checkThresholdConsistency(final Indicator entity) { - if (entity != null) { - final List emptyThresholds = entity.thresholds.stream() - .filter(t -> t.getValue() == null) - .collect(Collectors.toList()); - - if (!emptyThresholds.isEmpty()) { - throw new APIMessageException(APIMessage.fieldValidationError( - new FieldError( - Domain.EXAM.TYPE_NAME, - Domain.EXAM.ATTR_SUPPORTER, - "indicator:thresholds:thresholdEmpty"))); - } - - final Set values = entity.thresholds.stream() - .map(t -> t.getValue()) - .collect(Collectors.toSet()); - - if (values.size() != entity.thresholds.size()) { - throw new APIMessageException(APIMessage.fieldValidationError( - new FieldError( - Domain.EXAM.TYPE_NAME, - Domain.EXAM.ATTR_SUPPORTER, - "indicator:thresholds:thresholdDuplicate"))); - } - } - return Result.of(entity); - } - } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/InfoController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/InfoController.java index eae1ca9f..73ab5e0e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/InfoController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/InfoController.java @@ -24,10 +24,12 @@ import ch.ethz.seb.sebserver.gbl.model.EntityName; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; @WebServiceProfile @RestController @RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.INFO_ENDPOINT) +@SecurityRequirement(name = "oauth2") public class InfoController { private final InstitutionDAO institutionDAO; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/QuizController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/QuizController.java index 947d7fe3..ea84a216 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/QuizController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/QuizController.java @@ -32,10 +32,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.Authorization import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; @WebServiceProfile @RestController @RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.QUIZ_DISCOVERY_ENDPOINT) +@SecurityRequirement(name = "oauth2") public class QuizController { private final int defaultPageSize; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/SEBClientConfigController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/SEBClientConfigController.java index 605dd841..3aac116f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/SEBClientConfigController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/SEBClientConfigController.java @@ -37,6 +37,7 @@ import ch.ethz.seb.sebserver.gbl.Constants; 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.client.ClientCredentials; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM; import ch.ethz.seb.sebserver.gbl.model.Entity; @@ -81,6 +82,25 @@ public class SEBClientConfigController extends ActivatableEntityController ((SEBClientConfigDAO) this.entityDAO).getSEBClientCredentials(modelId)) + .getOrThrow(); + } + @RequestMapping( path = API.SEB_CLIENT_CONFIG_DOWNLOAD_PATH_SEGMENT + API.MODEL_ID_VAR_PATH_SEGMENT, method = RequestMethod.GET, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/AuthorizationServerConfig.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/AuthorizationServerConfig.java index 568391da..70b9f61a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/AuthorizationServerConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/AuthorizationServerConfig.java @@ -8,11 +8,16 @@ package ch.ethz.seb.sebserver.webservice.weblayer.oauth; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; +import org.springframework.http.MediaType; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; @@ -26,6 +31,7 @@ import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import ch.ethz.seb.sebserver.WebSecurityConfig; +import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.webservice.weblayer.WebServiceSecurityConfig; import ch.ethz.seb.sebserver.webservice.weblayer.WebServiceUserDetails; @@ -42,6 +48,8 @@ import ch.ethz.seb.sebserver.webservice.weblayer.WebServiceUserDetails; @Order(100) public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { + private static final Logger log = LoggerFactory.getLogger(AuthorizationServerConfig.class); + @Autowired private AccessTokenConverter accessTokenConverter; @Autowired(required = true) @@ -66,7 +74,15 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap oauthServer .tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()") - .passwordEncoder(this.clientPasswordEncoder); + .passwordEncoder(this.clientPasswordEncoder) + .authenticationEntryPoint((request, response, exception) -> { + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + log.warn( + "Unauthorized Request: {}", + exception != null ? exception.getMessage() : Constants.EMPTY_NOTE); + response.getOutputStream().println("{ \"error\": \"" + exception.getMessage() + "\" }"); + }); } @Override @@ -87,6 +103,7 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter); defaultTokenServices.setAccessTokenValiditySeconds(this.adminAccessTokenValSec); defaultTokenServices.setRefreshTokenValiditySeconds(this.adminRefreshTokenValSec); + defaultTokenServices.setClientDetailsService(this.webServiceClientDetails); endpoints .tokenStore(this.tokenStore) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/DefaultTokenServicesFallback.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/DefaultTokenServicesFallback.java index 74069bf5..9a98829f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/DefaultTokenServicesFallback.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/DefaultTokenServicesFallback.java @@ -21,6 +21,12 @@ public class DefaultTokenServicesFallback extends DefaultTokenServices { private static final Logger log = LoggerFactory.getLogger(DefaultTokenServicesFallback.class); + public DefaultTokenServicesFallback() { + super(); + super.setSupportRefreshToken(true); + super.setReuseRefreshToken(true); + } + @Override public OAuth2AccessToken createAccessToken(final OAuth2Authentication authentication) throws AuthenticationException { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/RevokeTokenEndpoint.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/RevokeTokenEndpoint.java index ea3c0c5a..9a46e86c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/RevokeTokenEndpoint.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/RevokeTokenEndpoint.java @@ -69,6 +69,18 @@ public class RevokeTokenEndpoint { } } + @EventListener(RevokeExamTokenEvent.class) + void revokeExamAccessToken(final RevokeExamTokenEvent event) { + final Collection tokens = this.tokenStore + .findTokensByClientId(event.clientId); + + if (tokens != null) { + for (final OAuth2AccessToken token : tokens) { + this.tokenStore.removeAccessToken(token); + } + } + } + public static final class RevokeTokenEvent extends ApplicationEvent { private static final long serialVersionUID = 5776699085388043743L; @@ -82,4 +94,17 @@ public class RevokeTokenEndpoint { } + public static final class RevokeExamTokenEvent extends ApplicationEvent { + + private static final long serialVersionUID = 5776699085388043743L; + + public final String clientId; + + public RevokeExamTokenEvent(final String clientId) { + super(clientId); + this.clientId = clientId; + } + + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebClientDetailsService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebClientDetailsService.java index ea1585f2..4bad3ce4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebClientDetailsService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebClientDetailsService.java @@ -11,7 +11,7 @@ package ch.ethz.seb.sebserver.webservice.weblayer.oauth; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; -import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.ClientRegistrationException; @@ -66,8 +66,10 @@ public class WebClientDetailsService implements ClientDetailsService { return getForExamClientAPI(clientId) .get(t -> { - log.error("Active ClientConfig not found: {} cause: {}", clientId, t.getMessage()); - throw new AccessDeniedException(t.getMessage()); + if (log.isDebugEnabled()) { + log.warn("Active ClientConfig not found: {} cause: {}", clientId, t.getMessage()); + } + throw new UsernameNotFoundException(t.getMessage()); }); } diff --git a/src/main/resources/config/application-dev-ws.properties b/src/main/resources/config/application-dev-ws.properties index 1ba4288f..6d85c2b3 100644 --- a/src/main/resources/config/application-dev-ws.properties +++ b/src/main/resources/config/application-dev-ws.properties @@ -25,7 +25,7 @@ sebserver.webservice.clean-db-on-startup=false # webservice configuration sebserver.init.adminaccount.gen-on-init=false -sebserver.webservice.distributed=false +sebserver.webservice.distributed=true #sebserver.webservice.master.delay.threshold=10000 sebserver.webservice.http.external.scheme=http sebserver.webservice.http.external.servername=localhost @@ -43,7 +43,6 @@ sebserver.webservice.api.exam.time-suffix=0 sebserver.webservice.api.exam.endpoint=/exam-api sebserver.webservice.api.exam.endpoint.discovery=${sebserver.webservice.api.exam.endpoint}/discovery sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1 -sebserver.webservice.api.exam.accessTokenValiditySeconds=3600 sebserver.webservice.api.exam.event-handling-strategy=ASYNC_BATCH_STORE_STRATEGY sebserver.webservice.api.exam.enable-indicator-cache=true sebserver.webservice.api.exam.defaultPingInterval=1000 @@ -53,6 +52,9 @@ sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token sebserver.webservice.lms.moodle.api.token.request.paths= sebserver.webservice.lms.address.alias=lms.mockup.com=lms.address.alias +springdoc.api-docs.enabled=true +springdoc.swagger-ui.enabled=true + # actuator configuration management.server.port=${server.port} management.endpoints.web.base-path=/management diff --git a/src/main/resources/config/application-dev.properties b/src/main/resources/config/application-dev.properties index e3848c27..fdaf8908 100644 --- a/src/main/resources/config/application-dev.properties +++ b/src/main/resources/config/application-dev.properties @@ -10,8 +10,8 @@ server.tomcat.uri-encoding=UTF-8 logging.level.ROOT=INFO logging.level.ch=INFO logging.level.ch.ethz.seb.sebserver.webservice.datalayer=INFO -logging.level.org.springframework.cache=INFO -logging.level.ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl=INFO +logging.level.org.springframework.cache=DEBUG +logging.level.ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl=DEBUG logging.level.ch.ethz.seb.sebserver.webservice.servicelayer.session=DEBUG logging.level.ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring=INFO logging.level.ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator=DEBUG @@ -22,8 +22,8 @@ logging.level.ch.ethz.seb.sebserver.webservice.weblayer.oauth=DEBUG #logging.level.ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordMapper=DEBUG #logging.level.ch.ethz.seb.sebserver.webservice.weblayer.api.ExamAPI_V1_Controller=TRACE logging.level.com.zaxxer.hikari=INFO +#logging.level.ch.ethz.seb.sebserver.webservice.servicelayer.dao=DEBUG sebserver.http.client.connect-timeout=15000 sebserver.http.client.connection-request-timeout=10000 sebserver.http.client.read-timeout=60000 - diff --git a/src/main/resources/config/application-ws.properties b/src/main/resources/config/application-ws.properties index b8d039a1..c354da07 100644 --- a/src/main/resources/config/application-ws.properties +++ b/src/main/resources/config/application-ws.properties @@ -45,6 +45,15 @@ sebserver.webservice.http.external.servername= sebserver.webservice.http.external.port= sebserver.webservice.http.redirect.gui=/gui +### Open API Documentation +springdoc.api-docs.enabled=false +springdoc.swagger-ui.enabled=false +springdoc.swagger-ui.oauth.clientId=guiClient +springdoc.swagger-ui.oauth.clientSecret=${sebserver.password} +#springdoc.consumes-to-match=application/json,application/x-www-form-urlencoded +#springdoc.default-consumes-media-type=application/x-www-form-urlencoded +springdoc.paths-to-exclude=/exam-api,/exam-api/discovery,/sebserver/error,/sebserver/check,/oauth,/exam-api/v1/* + ### webservice API sebserver.webservice.api.admin.clientId=guiClient sebserver.webservice.api.admin.endpoint=/admin-api/v1 @@ -60,8 +69,8 @@ sebserver.webservice.api.exam.config.init.permittedProcesses=config/initialPermi sebserver.webservice.api.exam.config.init.prohibitedProcesses=config/initialProhibitedProcesses.xml sebserver.webservice.api.exam.endpoint=/exam-api sebserver.webservice.api.exam.endpoint.discovery=${sebserver.webservice.api.exam.endpoint}/discovery -sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1 -sebserver.webservice.api.exam.accessTokenValiditySeconds=3600 +sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1 +sebserver.webservice.api.exam.accessTokenValiditySeconds=43200 sebserver.webservice.api.exam.event-handling-strategy=SINGLE_EVENT_STORE_STRATEGY sebserver.webservice.api.exam.enable-indicator-cache=true sebserver.webservice.api.pagination.maxPageSize=500 diff --git a/src/main/resources/config/ehcache.xml b/src/main/resources/config/ehcache.xml index a8577fce..8a12f3f4 100644 --- a/src/main/resources/config/ehcache.xml +++ b/src/main/resources/config/ehcache.xml @@ -97,7 +97,7 @@ java.lang.String ch.ethz.seb.sebserver.gbl.model.exam.QuizData - 10 + 5 10000 diff --git a/src/main/resources/config/sql/base/V15__alterTables_v1_4.sql b/src/main/resources/config/sql/base/V15__alterTables_v1_4.sql new file mode 100644 index 00000000..6edef22b --- /dev/null +++ b/src/main/resources/config/sql/base/V15__alterTables_v1_4.sql @@ -0,0 +1,38 @@ +-- ----------------------------------------------------- +-- Alter Table `batch_action` +-- ----------------------------------------------------- + +ALTER TABLE `batch_action` +MODIFY `source_ids` VARCHAR(4000) NULL, +ADD COLUMN IF NOT EXISTS `owner` VARCHAR(255) NULL AFTER `institution_id`, +ADD COLUMN IF NOT EXISTS `attributes` VARCHAR(4000) NULL AFTER `action_type` +; + +-- ----------------------------------------------------- +-- Alter Table `configuration_node` +-- ----------------------------------------------------- + +ALTER TABLE `configuration_node` +ADD COLUMN IF NOT EXISTS `last_update_time` BIGINT UNSIGNED NULL AFTER `status`, +ADD COLUMN IF NOT EXISTS `last_update_user` VARCHAR(255) NULL AFTER `last_update_time` +; + +-- ----------------------------------------------------- +-- Alter Table `seb_client_configuration` +-- ----------------------------------------------------- + +ALTER TABLE `seb_client_configuration` +ADD COLUMN IF NOT EXISTS `last_update_time` BIGINT UNSIGNED NULL AFTER `active`, +ADD COLUMN IF NOT EXISTS `last_update_user` VARCHAR(255) NULL AFTER `last_update_time` +; + +-- ----------------------------------------------------- +-- Alter Table `exam` +-- ----------------------------------------------------- + +ALTER TABLE `exam` +ADD COLUMN IF NOT EXISTS `quiz_name` VARCHAR(255) NULL AFTER `last_modified`, +ADD COLUMN IF NOT EXISTS `quiz_start_time` DATETIME NULL AFTER `quiz_name`, +ADD COLUMN IF NOT EXISTS `quiz_end_time` DATETIME NULL AFTER `quiz_start_time`, +ADD COLUMN IF NOT EXISTS `lms_available` INT(1) NULL AFTER `quiz_end_time` +; \ No newline at end of file diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index f8e25645..56857795 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -22,6 +22,7 @@ sebserver.overall.action.close=Close sebserver.overall.action.goAwayFromEditPageConfirm=Are you sure you want to leave this page? Unsaved data will be lost. sebserver.overall.action.category.varia= sebserver.overall.action.category.filter= +sebserver.overall.action.toomanyselection=There is only one selection allowed for this action. Please select only one entry sebserver.overall.action.showPassword.tooltip=Show / hide password in plain text @@ -48,6 +49,7 @@ sebserver.overall.types.activityType.ACTIVATE=Activate sebserver.overall.types.activityType.DELETE=Delete sebserver.overall.types.activityType.LOGIN=Login sebserver.overall.types.activityType.LOGOUT=Logout +sebserver.overall.types.activityType.FINISHED=Finished sebserver.overall.types.entityType.CONFIGURATION_ATTRIBUTE=Configuration Attribute sebserver.overall.types.entityType.CONFIGURATION_VALUE=Configuration Value @@ -69,12 +71,15 @@ sebserver.overall.types.entityType.EXAM_SEB_RESTRICTION=SEB Exam Restriction sebserver.overall.types.entityType.CERTIFICATE=Certificate sebserver.overall.types.entityType.EXAM_TEMPLATE=Exam Template sebserver.overall.types.entityType.EXAM_PROCTOR_DATA=Exam Proctoring Settings +sebserver.overall.types.entityType.BATCH_ACTION=Batch Action sebserver.overall.activity.title.serveradmin=SEB Server Administration sebserver.overall.activity.title.sebconfig=Configurations sebserver.overall.activity.title.examadmin=Exam Administration sebserver.overall.activity.title.monitoring=Monitoring +sebserver.overall.batchaction.selected=Selected + ################################ # Form validation and messages ################################ @@ -97,7 +102,7 @@ sebserver.form.validation.fieldError.serverNotAvailable=No service seems to be a sebserver.form.validation.fieldError.url.invalid=Invalid URL. The given URL cannot be reached. sebserver.form.validation.fieldError.url.noservice=The expected service is not available within the given URL and API access. sebserver.form.validation.fieldError.thresholdDuplicate=There are duplicate threshold values. -sebserver.form.validation.fieldError.thresholdEmpty=There are empty threshold entries. +sebserver.form.validation.fieldError.thresholdEmpty=There are missing values or colors for the threshold declaration sebserver.error.unexpected=Unexpected Error sebserver.page.message=Information sebserver.dialog.confirm.title=Confirmation @@ -191,6 +196,18 @@ sebserver.institution.form.logoImage.tooltip=The Image that is shown as a logo i sebserver.institution.form.logoImage.unsupportedFileType=The selected file is not supported. Supported are: PNG and JPG +sebserver.institution.action.delete=Delete Institution +sebserver.institution.delete.form.title=Delete Institution +sebserver.institution.delete.form.info=Please Note:
    This deletes the institution and all related object like LMS Setup, exams and local import of a course
    or quiz in SEB Server that belongs to this institution
    This will not delete any course or quiz on a Learning Management System (LMS). +sebserver.institution.delete.report.info=The following dependencies will be deleted within this institution deletion.
Please check them carefully before delete. +sebserver.institution.delete.report.list.type=Type +sebserver.institution.delete.report.list.name=Name +sebserver.institution.delete.report.list.description=Description +sebserver.institution.delete.action.delete=Delete All +sebserver.institution.delete.confirm.title=Deletion Successful +sebserver.institution.delete.confirm.message=The institution ({0}) was successfully deleted.
Also the following number of dependencies where successfully deleted: {1}.

And there where {2} errors. +sebserver.institution.delete.report.list.empty=No dependencies will be deleted. + ################################ # User Account ################################ @@ -259,8 +276,8 @@ sebserver.useraccount.form.timezone=Time Zone sebserver.useraccount.form.timezone.tooltip=The time-zone of the user

Note that this also defines the exact time and date that is displayed to the user sebserver.useraccount.form.roles=User Roles sebserver.useraccount.form.roles.tooltip=The roles of the user
A user can have more then one role but must have at least one.

In Edit mode, please use the tooltip on the role name for more information about a specific role. -sebserver.useraccount.form.password=Password -sebserver.useraccount.form.password.tooltip=The password of the user account +sebserver.useraccount.form.password=Your Password +sebserver.useraccount.form.password.tooltip=The password of your user account sebserver.useraccount.form.password.confirm=Confirm Password sebserver.useraccount.form.password.confirm.tooltip=Please confirm the password sebserver.useraccount.form.pwchange.title=Change Password : {0} @@ -268,6 +285,7 @@ sebserver.useraccount.form.password.new=New Password sebserver.useraccount.form.password.new.tooltip=The new password for the user account sebserver.useraccount.form.password.new.confirm=Confirm New Password sebserver.useraccount.form.password.new.confirm.tooltip=Please confirm the password +sebserver.useraccount.form.password.info=To change the password of user with account {0},
please enter your own password first and then the new password for the user account and confirm the new password. sebserver.useraccount.delete.form.title=Delete User Account sebserver.useraccount.delete.form.info=Please Note:
    This deletes the particular User Account with all selected dependencies.
    The User Account and selected dependent exams and exam configurations, will be lost after deletion.
    Please use the "Show Report" action below to see a report of all objects that will be
    deleted and check them carefully. @@ -362,6 +380,18 @@ sebserver.lmssetup.form.proxy.password=Proxy Password sebserver.lmssetup.form.proxy.password.tooltip=Proxy authentication password, needed if the proxy requests authentication sebserver.lmssetup.form.proxy.auth-credentials.tooltip=The proxy authentication credentials (name and password)
to authenticate the connection within the proxy server +sebserver.lmssetup.action.delete=Delete LMS Setup +sebserver.lmssetup.delete.form.title=Delete LMS Setup +sebserver.lmssetup.delete.form.info=Please Note:
    This deletes the LMS Setup and all exams and local import of a
     course or quiz in SEB Server that belongs to this LMS Setup
    This will not delete any course or quiz on a Learning Management System (LMS). +sebserver.lmssetup.delete.report.info=The following dependencies will be deleted within this LMS Setup deletion.
Please check them carefully before delete. +sebserver.lmssetup.delete.report.list.type=Type +sebserver.lmssetup.delete.report.list.name=Name +sebserver.lmssetup.delete.report.list.description=Description +sebserver.lmssetup.delete.action.delete=Delete All +sebserver.lmssetup.delete.confirm.title=Deletion Successful +sebserver.lmssetup.delete.confirm.message=The LMS Setup ({0}) was successfully deleted.
Also the following number of dependencies where successfully deleted: {1}.

And there where {2} errors. +sebserver.lmssetup.delete.report.list.empty=No dependencies will be deleted. + ################################ #LMS Exam ################################ @@ -436,6 +466,8 @@ sebserver.exam.list.column.starttime=Start Time {0} sebserver.exam.list.column.starttime.tooltip=The start time of the exam

Use the filter above to set a specific from date
{0} sebserver.exam.list.column.type=Type sebserver.exam.list.column.type.tooltip=The type of the exam

Use the filter above to set a specific exam type
{0} +sebserver.exam.list.column.state=Status +sebserver.exam.list.column.state.tooltip=The current status of the exam

Use the filter above to set a specific exam status
{0} sebserver.exam.list.empty=No Exam can be found. Please adapt the filter or import one from LMS sebserver.exam.list.modify.out.dated=Finished exams cannot be modified. @@ -462,6 +494,8 @@ sebserver.exam.action.activate=Activate Exam sebserver.exam.action.deactivate=Deactivate Exam sebserver.exam.action.delete=Delete Exam sebserver.exam.action.delete.consistency.error=Deletion Failed.
Please make sure there are no active SEB clients connected to the exam before deletion. +sebserver.exam.action.archive=Archive +sebserver.exam.action.archive.confirm=An archived exam cannot be rerun and will remain in read-only view.

Are you sure to archive the exam? sebserver.exam.action.sebrestriction.enable=Apply SEB Lock sebserver.exam.action.sebrestriction.disable=Release SEB Lock sebserver.exam.action.sebrestriction.details=SEB Restriction Details @@ -554,6 +588,7 @@ sebserver.exam.type.VDI.tooltip=Exam type specified for Virtual Desktop Infrastr sebserver.exam.status.UP_COMING=Up Coming sebserver.exam.status.RUNNING=Running sebserver.exam.status.FINISHED=Finished +sebserver.exam.status.ARCHIVED=Archived sebserver.exam.status.CORRUPT_NO_LMS_CONNECTION=Corrupt (No LMS Connection) sebserver.exam.status.CORRUPT_INVALID_ID=Corrupt (Invalid Identifier) @@ -730,6 +765,8 @@ sebserver.clientconfig.form.title.subtitle= sebserver.clientconfig.form.name=Name sebserver.clientconfig.form.name.tooltip=The name of the connection configuration.
Any name that not already is in use for another connection configuration +sebserver.clientconfig.form.update.time Last Update +sebserver.clientconfig.form.update.user=Last Update By sebserver.clientconfig.form.pinginterval=Ping Interval sebserver.clientconfig.form.pinginterval.tooltip=Defines an interval time in milliseconds for a SEB client to send a ping to the SEB Server sebserver.clientconfig.form.vditype=VDI Setup @@ -773,6 +810,11 @@ sebserver.clientconfig.form.certificate.tooltip=Choose identity certificate to b sebserver.clientconfig.form.type.async=Use asymmetric-only encryption sebserver.clientconfig.form.type.async.tooltip=Use old asymmetric-only encryption (for SEB < 2.2) +sebserver.clientconfig.form.credentials.title=Client Credentials of Connection Configuration +sebserver.clientconfig.form.credentials.info=A SEB client that loads this connection configuration
uses the following credentials to securely connect to the SEB Server. +sebserver.clientconfig.form.credentials.name=Client Id +sebserver.clientconfig.form.credentials.secret=Secret + sebserver.clientconfig.config.purpose.START_EXAM=Starting an Exam sebserver.clientconfig.config.purpose.START_EXAM.tooltip=If the connection configuration is loaded via a SEB-Link, the local configuration will not be overwritten. sebserver.clientconfig.config.purpose.CONFIGURE_CLIENT=Configure a Client @@ -783,9 +825,13 @@ sebserver.clientconfig.action.list.view=View Connection Configuration sebserver.clientconfig.action.list.modify=Edit Connection Configuration sebserver.clientconfig.action.modify=Edit Connection Configuration sebserver.clientconfig.action.save=Save Connection Configuration +sebserver.clientconfig.action.credentials=Show Client Credentials sebserver.clientconfig.action.activate=Activate Connection Configuration sebserver.clientconfig.action.deactivate=Deactivate Connection Configuration sebserver.clientconfig.action.export=Export Connection Configuration +sebserver.clientconfig.action.delete=Delete Connection Configuration +sebserver.clientconfig.action.delete.confirm=Please note that after deletion of this Connection Configuration, a SEB that loads a former download of this configuration,
either from this page or from the exam page as Exam Connection Configuration,
Connection Configuration will never be able to connect to SEB Server again. +sebserver.clientconfig.action.delete.success=Connection Configuration successfully deleted ################################ # SEB Exam Configuration @@ -802,6 +848,8 @@ sebserver.examconfig.list.column.description=Description sebserver.examconfig.list.column.description.tooltip=The description of the SEB exam configuration

Use the filter above to find configurations that contain specific words or phrases within the description.
{0} sebserver.examconfig.list.column.status=Status sebserver.examconfig.list.column.status.tooltip=The status of the SEB exam configuration

Use the filter above to specify a status
{0} +sebserver.examconfig.list.column.template=Template +sebserver.examconfig.list.column.template.tooltip=The origin template of the SEB exam configuration

Use the filter above to specify a template
{0} sebserver.examconfig.list.actions= @@ -810,6 +858,17 @@ sebserver.examconfig.info.pleaseSelect=At first please select an Exam Configurat sebserver.examconfig.list.action.no.modify.privilege=No Access: An Exam Configuration from other institution cannot be modified. sebserver.examconfig.action.list.new=Add Exam Configuration sebserver.examconfig.action.list.view=View Exam Configuration +sebserver.examconfig.list.action.statechange=State Change +sebserver.examconfig.list.batch.statechange.title=State Change All +sebserver.examconfig.list.batch.action.statechange=Change States +sebserver.examconfig.list.batch.progress=Bulk State Change in Progress: {0}% +sebserver.examconfig.list.batch.finished=Bulk State Change finished. Successful: {0}.Failed: {1} +sebserver.examconfig.list.batch.action.statechange.info=This action changes all selected exam configurations to a defined target state.
Please note that this will be done only for those that can be changed to the defined target state in regard to its current state. +sebserver.examconfig.list.batch.action.status=Target Status +sebserver.examconfig.list.action.reset=Reset To Template Settings +sebserver.examconfig.list.batch.reset.title=Reset To Template Settings +sebserver.examconfig.list.batch.action.reset=Reset All +sebserver.examconfig.list.batch.action.reset.info=This action process a reset of the SEB settings to its template settings for all selected configurations.
Please note this will be done only for those that has an origin template and are not in state "Used".
Please note also that this action tries to directly publish the changes and if not possible (active SEB client connections) it will report an error even if the settings has been reseted. sebserver.examconfig.action.list.modify.properties=Edit Exam Configuration sebserver.examconfig.action.delete=Delete Exam Configuration @@ -845,7 +904,9 @@ sebserver.examconfig.message.confirm.delete=This will completely delete the exam sebserver.examconfig.message.consistency.error=The exam configuration cannot be deleted since it is used by at least one running or upcoming exam.
Please remove the exam configuration from running and upcoming exams first. sebserver.examconfig.message.delete.confirm=The exam configuration ({0}) was successfully deleted. sebserver.examconfig.message.delete.partialerror=The exam configuration ({0}) was deleted but there where some dependency errors:

{1} - +sebserver.examconfig.action.restore.template.settings=Reset To Template Settings +sebserver.examconfig.action.restore.template.settings.success=Configuration settings successfully restored to template defaults +sebserver.examconfig.action.restore.template.settings.confirm=Are you sure to reset this configuration setting to the templates settings? sebserver.examconfig.form.title.new=Add Exam Configuration sebserver.examconfig.form.title=Exam Configuration @@ -862,6 +923,9 @@ sebserver.examconfig.form.status.tooltip=The status of this SEB exam configurati sebserver.examconfig.form.config-key.title=Config Key sebserver.examconfig.form.attached-to=Attached To Exam sebserver.examconfig.form.attached-to.tooltip=This SEB exam configuration is currently attached to the following exams.

Select an exam from the list and use the "View Exam" or Double-Click on the list to go to a specific exam. +sebserver.examconfig.form.update.time=Last Update +sebserver.examconfig.form.update.user=Last Update By + sebserver.examconfig.status.CONSTRUCTION=Under Construction sebserver.examconfig.status.READY_TO_USE=Ready To Use @@ -1082,7 +1146,7 @@ sebserver.examconfig.props.label.browserUserAgentMac.0=Default (depends on insta sebserver.examconfig.props.label.browserUserAgentMac.1=Custom sebserver.examconfig.props.label.browserUserAgentMac.1.tooltip=Zoom only text on web pages using Ctrl-Mousewheel (Win) -sebserver.examconfig.props.label.enableSebBrowser=Enable SEB with browser window +sebserver.examconfig.props.label.enableSebBrowser=Use SEB without browser window sebserver.examconfig.props.label.enableSebBrowser.tooltip=Disable this to start another application in kiosk mode
(for example a virtual desktop infrastructure client) sebserver.examconfig.props.label.browserWindowTitleSuffix=Suffix to be added to every browser window @@ -1603,7 +1667,11 @@ sebserver.configtemplate.attrs.list.group=Group sebserver.configtemplate.attrs.list.group.tooltip=The group on the view/tab where the exam configuration attribute belongs to

{0} sebserver.configtemplate.attrs.list.type=Type sebserver.configtemplate.attrs.list.type.tooltip=The type of the exam configuration attribute

{0} +sebserver.configtemplate.attrs.list.value=Value +sebserver.configtemplate.attrs.list.value.tooltip=The settings value that is set as default value for this template +sebserver.configtemplate.action.error.title=Action Failed +sebserver.configtemplate.action.error.noview.message=This setting has no default view
and cannot be attached/detached from a view sebserver.configtemplate.attr.list.actions= sebserver.configtemplate.attr.list.actions.modify=Edit Attribute sebserver.configtemplate.attr.list.actions.setdefault=Set Default Values @@ -1611,6 +1679,7 @@ sebserver.configtemplate.attr.list.actions.removeview=Remove From View sebserver.configtemplate.attr.list.actions.attach-default-view=Attach To View sebserver.configtemplate.attr.info.pleaseSelect=At first please select an Attribute from the list + sebserver.configtemplate.attr.form.title=Configuration Template Attribute sebserver.configtemplate.attr.form.title.subtitle= sebserver.configtemplate.attr.form.name=Name @@ -1627,6 +1696,11 @@ sebserver.configtemplate.attr.form.value.tooltip=The SEB exam configuration attr sebserver.configtemplate.attr.action.setdefault=Set Default Values sebserver.configtemplate.attr.action.template=View Configuration Template +sebserver.configtemplate.action.delete=Delete Configuration Template +sebserver.configtemplate.message.confirm.delete=This will completely delete the configuration template
and reset all exam configuration as well as exam templates that uses this template to the default template

Are you sure you want to delete this configuration template? +sebserver.configtemplate.message.delete.confirm=The configuration template ({0}) was successfully deleted. +sebserver.configtemplate.message.delete.partialerror=The configuration template ({0}) was deleted but there where some dependency errors:

{1} + ################################ # Exam Template ################################ @@ -1692,7 +1766,7 @@ sebserver.examtemplate.indicator.action.list.new=Add Indicator sebserver.examtemplate.indicator.action.list.modify=Edit Indicator sebserver.examtemplate.indicator.action.list.delete=Delete Indicator - +sebserver.examtemplate.proctoring.actions.open=Proctoring Settings ################################ # Certificates @@ -1804,6 +1878,9 @@ sebserver.monitoring.exam.connection.action.instruction.quit.all=Quit All SEB Cl sebserver.monitoring.exam.connection.action.instruction.quit.confirm=Are you sure to quit this SEB client connection? sebserver.monitoring.exam.connection.action.instruction.quit.selected.confirm=Are you sure to quit all selected, active SEB client connections? sebserver.monitoring.exam.connection.action.instruction.quit.all.confirm=Are you sure to quit all active SEB client connections? +sebserver.monitoring.exam.connection.action.instruction.lock=Lock SEB Client +sebserver.monitoring.exam.connection.action.instruction.lock.selected=Lock Selected SEB Clients +sebserver.monitoring.exam.connection.action.instruction.lock.confirm=Are you sure to lock this SEB client connection? sebserver.monitoring.exam.connection.action.instruction.disable.selected.confirm=Are you sure to disable all selected SEB client connections? sebserver.monitoring.exam.connection.action.instruction.disable.all.confirm=Are you sure to disable all active SEB client connections? sebserver.monitoring.exam.connection.action.disable=Mark As Canceled @@ -1822,6 +1899,8 @@ sebserver.monitoring.exam.connection.action.proctoring.examroom=Exam Room Procto sebserver.monitoring.exam.connection.action.openTownhall.confirm=You are about to open the town-hall room and force all SEB clients to join the town-hall room.
Are you sure to open the town-hall? sebserver.monitoring.exam.connection.action.closeTownhall.confirm=You are about to close the town-hall room and force all SEB clients to join it's proctoring room.
Are you sure to close the town-hall? sebserver.monitoring.exam.connection.action.singleroom.confirm=You are about to open the single/one to one room for this participant.
Are you sure you want to open the single room? +sebserver.monitoring.exam.connection.actions.group2=  +sebserver.monitoring.exam.connection.actions.group3=  sebserver.monitoring.exam.connection.notificationlist.actions= sebserver.monitoring.exam.connection.action.confirm.notification=Confirm Notification @@ -1831,7 +1910,6 @@ sebserver.monitoring.exam.connection.notificationlist.pleaseSelect=At first plea sebserver.monitoring.exam.connection.notificationlist.title=Pending Notification sebserver.monitoring.exam.connection.notificationlist.title.tooltip=All pending notifications sent by the SEB Client - sebserver.monitoring.exam.connection.eventlist.title=Events sebserver.monitoring.exam.connection.eventlist.title.tooltip=All events and logs sent by the SEB Client sebserver.monitoring.exam.connection.eventlist.empty=No event found @@ -1868,6 +1946,72 @@ sebserver.monitoring.exam.connection.status.CLOSED=Closed sebserver.monitoring.exam.connection.status.ABORTED=Aborted sebserver.monitoring.exam.connection.status.DISABLED=Canceled +sebserver.monitoring.lock.title=Lock SEB Clients +sebserver.monitoring.lock.form.info.title=Info +sebserver.monitoring.lock.form.info=Please check all selected SEB connection
before sending lock by click on "OK" +sebserver.monitoring.lock.form.message=Lock Message +sebserver.monitoring.lock.form.message.tooltip=A message that will be displayed by the SEB with the lock screen to the user +sebserver.monitoring.lock.list.title=Selected SEB Client Connections +sebserver.monitoring.lock.list.name=SEB User Session Identifier +sebserver.monitoring.lock.list.info=SEB Connection Info +sebserver.monitoring.lock.noselection=Please select at least one active SEB client connection. + +################################ +# Finished Exams +################################ +sebserver.finished.action.list=Finished Exams +sebserver.finished.exam.list.title=Finished Exams +sebserver.finished.exam.list.actions= + +sebserver.finished.exam.info.pleaseSelect=At first please select an Exam from the list +sebserver.finished.exam.list.empty=There are currently no finished exams + +sebserver.finished.exam.list.column.name=Name +sebserver.finished.exam.list.column.name.tooltip=The name of the exam

Use the filter above to narrow down to a specific exam name
{0} +sebserver.finished.exam.list.column.state=State +sebserver.finished.exam.list.column.state.tooltip=The state of the finished exam

Use the filter above to see only archived or finished exams +sebserver.finished.exam.list.column.type=Type +sebserver.finished.exam.list.column.type.tooltip=The type of the exam

Use the filter above to set a specific exam type
{0} +sebserver.finished.exam.list.column.startTime=Start Time {0} +sebserver.finished.exam.list.column.startTime.tooltip=The start date and time of the exam

{0} +sebserver.finished.exam.list.column.endTime=End Time {0} +sebserver.finished.exam.list.column.endTime.tooltip=The end date and time of the exam

{0} +sebserver.finished.exam.action.list.view=View Finished Exam + +sebserver.finished.exam.connections.title=Finished Exam ({0}) +sebserver.finished.exam.connections.action=Search +sebserver.finished.exam.connections.empty=No Client Connections available +sebserver.finished.exam.connections.name=Session or User Name +sebserver.finished.exam.connections.info=Connection Info +sebserver.finished.exam.connections.status=Status +sebserver.finished.exam.connection.emptySelection=At first please select a Connection from the list +sebserver.finished.exam.connection.action.view=View Details + +sebserver.finished.exam.connection.title=SEB Client Connection +sebserver.finished.connection.form.id=User Name or Session +sebserver.finished.connection.form.id.tooltip=The user session identifier or username sent by the SEB client after LMS login +sebserver.finished.connection.form.info=Connection Info +sebserver.finished.connection.form.info.tooltip=Format: IP Address,SEB Version, OSName +sebserver.finished.connection.form.status=Status +sebserver.finished.connection.form.status.tooltip=The current connection status +sebserver.finished.connection.form.exam=Exam +sebserver.finished.connection.form.exam.tooltip=The exam name + +sebserver.finished.exam.connection.eventlist.title=Events +sebserver.finished.exam.connection.eventlist.title.tooltip=All events and logs sent by the SEB Client +sebserver.finished.exam.connection.eventlist.empty=No event found +sebserver.finished.exam.connection.eventlist.type=Event Type +sebserver.finished.exam.connection.eventlist.type.tooltip=The type of the log event

Use the filter above to set a specific event type
{0} +sebserver.finished.exam.connection.eventlist.clienttime=Client Time {0} +sebserver.finished.exam.connection.eventlist.clienttime.tooltip=The time the SEB client has sent within the log event

{0} +sebserver.finished.exam.connection.eventlist.servertime=Server Time {0} +sebserver.finished.exam.connection.eventlist.servertime.tooltip=The exact time (UTC) the SEB Server has received the log event

{0} +sebserver.finished.exam.connection.eventlist.value=Value +sebserver.finished.exam.connection.eventlist.value.tooltip=The value of the log event

{0} +sebserver.finished.exam.connection.eventlist.text=Text +sebserver.finished.exam.connection.eventlist.text.tooltip=The text of the log event

{0} +sebserver.finished.exam.action.detail.view=Back To Exam + ################################ # Logs ################################ diff --git a/src/main/resources/static/css/sebserver.css b/src/main/resources/static/css/sebserver.css index 2be30294..c41a0838 100644 --- a/src/main/resources/static/css/sebserver.css +++ b/src/main/resources/static/css/sebserver.css @@ -890,6 +890,14 @@ TableItem { background-image: none; } +TableItem.disabled { + background-color: transparent; + color: #aaaaaa; + text-decoration: none; + text-shadow: none; + background-image: none; +} + Table-RowOverlay.warning { background-color: rgba( 168, 50, 45, 0.5 ); background-gradient-color: rgba( 168, 50, 45, 0.5 ); @@ -901,6 +909,11 @@ TableItem:linesvisible:even { color: inherit; } +TableItem:linesvisible:even.disabled { + background-color: #ffffff; + color: #aaaaaa; +} + Table-RowOverlay { background-color: transparent; color: inherit; diff --git a/src/main/resources/static/images/archive.png b/src/main/resources/static/images/archive.png new file mode 100644 index 00000000..bcf45078 Binary files /dev/null and b/src/main/resources/static/images/archive.png differ diff --git a/src/test/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreakerTest.java b/src/test/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreakerTest.java index ee69e441..62769d00 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreakerTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gbl/async/CircuitBreakerTest.java @@ -39,10 +39,61 @@ public class CircuitBreakerTest { assertNotNull(this.asyncService); } + @Test + public void testMaxAttempts1() { + final CircuitBreaker circuitBreaker = + this.asyncService.createCircuitBreaker(1, 500, 1000); + + final AtomicInteger attemptCounter = new AtomicInteger(0); + final Supplier tester = () -> { + attemptCounter.getAndIncrement(); + throw new RuntimeException("Test Error"); + }; + + final Result result = circuitBreaker.protectedRun(tester); // 1. call... + assertTrue(result.hasError()); + assertEquals(State.HALF_OPEN, circuitBreaker.getState()); + assertEquals("1", String.valueOf(attemptCounter.get())); + } + + @Test + public void testMaxAttempts4() { + final CircuitBreaker circuitBreaker = + this.asyncService.createCircuitBreaker(4, 500, 1000); + + final AtomicInteger attemptCounter = new AtomicInteger(0); + final Supplier tester = () -> { + attemptCounter.getAndIncrement(); + throw new RuntimeException("Test Error"); + }; + + final Result result = circuitBreaker.protectedRun(tester); // 1. call... + assertTrue(result.hasError()); + assertEquals(State.HALF_OPEN, circuitBreaker.getState()); + assertEquals("4", String.valueOf(attemptCounter.get())); + } + + @Test + public void testMaxAttempts0() { + final CircuitBreaker circuitBreaker = + this.asyncService.createCircuitBreaker(0, 500, 1000); + + final AtomicInteger attemptCounter = new AtomicInteger(0); + final Supplier tester = () -> { + attemptCounter.getAndIncrement(); + throw new RuntimeException("Test Error"); + }; + + final Result result = circuitBreaker.protectedRun(tester); // 1. call... + assertTrue(result.hasError()); + assertEquals(State.HALF_OPEN, circuitBreaker.getState()); + assertEquals("1", String.valueOf(attemptCounter.get())); + } + @Test public void roundtrip1() throws InterruptedException { final CircuitBreaker circuitBreaker = - this.asyncService.createCircuitBreaker(3, 500, 1000); + this.asyncService.createCircuitBreaker(4, 500, 1000); final Supplier tester = tester(100, 5, 10); @@ -73,7 +124,7 @@ public class CircuitBreakerTest { Thread.sleep(100); result = circuitBreaker.protectedRun(tester); // 10. call... assertEquals(State.OPEN, circuitBreaker.getState()); - assertEquals(CircuitBreaker.OPEN_STATE_EXCEPTION, result.getError()); + assertEquals(CircuitBreaker.OPEN_CIRCUIT_BREAKER_EXCEPTION, result.getError().getMessage()); // wait time to recover Thread.sleep(1000); diff --git a/src/test/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreakerTest.java b/src/test/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreakerTest.java index 6b96f907..c7990b7a 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreakerTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gbl/async/MemoizingCircuitBreakerTest.java @@ -42,7 +42,7 @@ public class MemoizingCircuitBreakerTest { @Test public void roundtrip1() throws InterruptedException { final MemoizingCircuitBreaker circuitBreaker = this.asyncService.createMemoizingCircuitBreaker( - tester(100, 5, 10), 3, 500, 1000, true, 1000); + tester(100, 5, 10), 4, 500, 1000, true, 1000); assertNull(circuitBreaker.getCached()); diff --git a/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java b/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java index de442a7d..2652e7ac 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java +++ b/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java @@ -129,13 +129,15 @@ public class ModelObjectJSONGenerator { "encryptSecretConfirm", "certAlias", false, - true); + true, + DateTime.now(), + "user123"); System.out.println(domainObject.getClass().getSimpleName() + ":"); System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject)); domainObject = new ConfigurationNode( 1L, 1L, 1L, "name", "description", ConfigurationType.EXAM_CONFIG, "ownerUUID", - ConfigurationStatus.CONSTRUCTION); + ConfigurationStatus.CONSTRUCTION, DateTime.now(), "user123"); System.out.println(domainObject.getClass().getSimpleName() + ":"); System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject)); @@ -193,8 +195,8 @@ public class ModelObjectJSONGenerator { System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject)); domainObject = new Exam( - 1L, 1L, 1L, "externalId", "name", "description", DateTime.now(), DateTime.now(), - "startURL", ExamType.BYOD, "owner", + 1L, 1L, 1L, "externalId", true, "name", DateTime.now(), DateTime.now(), + ExamType.BYOD, "owner", Arrays.asList("user1", "user2"), ExamStatus.RUNNING, false, "browserExamKeys", true, null, null, null, null); System.out.println(domainObject.getClass().getSimpleName() + ":"); @@ -209,7 +211,7 @@ public class ModelObjectJSONGenerator { System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject)); domainObject = new ExamConfigurationMap( - 1L, 1L, 1L, "examName", "examDescription", DateTime.now(), ExamType.BYOD, + 1L, 1L, 1L, "examName", "examDescription", DateTime.now(), ExamType.BYOD, ExamStatus.RUNNING, 1L, "userNames", "encryptSecret", "confirmEncryptSecret", "configName", "configDescription", ConfigurationStatus.IN_USE); System.out.println(domainObject.getClass().getSimpleName() + ":"); diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/ClientConfigTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/ClientConfigTest.java index b51308a3..f5aabb5a 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/ClientConfigTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/ClientConfigTest.java @@ -126,6 +126,8 @@ public class ClientConfigTest extends GuiIntegrationTest { null, "certAlias", false, + null, + null, null)) .call(); @@ -157,6 +159,8 @@ public class ClientConfigTest extends GuiIntegrationTest { "password", "certAlias", false, + null, + null, null)) .call() .getOrThrow(); diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/FinishedExamTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/FinishedExamTest.java new file mode 100644 index 00000000..b4ba156d --- /dev/null +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/FinishedExamTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 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.integration; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +import java.io.IOException; + +import org.junit.Test; +import org.springframework.test.context.jdbc.Sql; + +import ch.ethz.seb.sebserver.gbl.model.Page; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestServiceImpl; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamClientConnection; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamClientConnectionPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamPage; + +@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" }) +public class FinishedExamTest extends GuiIntegrationTest { + + @Test + public void finishedExamsTest() throws IOException { + final RestServiceImpl restService = createRestServiceForUser( + "admin", + "admin", + new GetFinishedExamPage(), + new GetFinishedExamClientConnectionPage(), + new GetFinishedExamClientConnection()); + + // get finished exams page: + final Page finishedExams = restService + .getBuilder(GetFinishedExamPage.class) + .call() + .getOrThrow(); + + assertNotNull(finishedExams); + assertFalse(finishedExams.content.isEmpty()); + } + +} diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/GuiIntegrationTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/GuiIntegrationTest.java index 5ff8dada..a002194d 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/GuiIntegrationTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/GuiIntegrationTest.java @@ -41,7 +41,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.WebserviceInfoDAO; @RunWith(SpringRunner.class) @SpringBootTest( - properties = "file.encoding=UTF-8", + properties = { "file.encoding=UTF-8" }, classes = SEBServer.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @ActiveProfiles("test") @@ -62,15 +62,15 @@ public abstract class GuiIntegrationTest { @Autowired protected FilterChainProxy springSecurityFilterChain; @Autowired - private WebserviceInfoDAO webserviceInfoDAO; + protected WebserviceInfoDAO webserviceInfoDAO; @Autowired - private WebserviceInfo webserviceInfo; + protected WebserviceInfo webserviceInfo; protected MockMvc mockMvc; @Before public void setup() { - this.webserviceInfoDAO.unregister(this.webserviceInfo.getWebserviceUUID()); + //this.webserviceInfoDAO.unregister(this.webserviceInfo.getWebserviceUUID()); this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac) .addFilter(this.springSecurityFilterChain).build(); diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/SEBClientBot.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/SEBClientBot.java index 05df858e..d45fb916 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/SEBClientBot.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/SEBClientBot.java @@ -96,10 +96,15 @@ public class SEBClientBot { long errorInterval = ONE_SECOND; long warnInterval = ONE_SECOND / 2; long notificationInterval = 800; - long runtime = ONE_SECOND * 3; + long runtime = ONE_SECOND * 5; int connectionAttempts = 1; - public SEBClientBot(final String clientId, final String clientSecret, final String examId, final String instId) + public SEBClientBot( + final String clientId, + final String clientSecret, + final String examId, + final String instId, + final boolean wait) throws Exception { this.clientId = clientId; @@ -114,7 +119,13 @@ public class SEBClientBot { ? this.sessionId : "connection_" + getRandomName(); - new ConnectionBot(sessionId).run(); + if (wait) { + new ConnectionBot(sessionId).run(); + } else { + new Thread(new ConnectionBot(sessionId)).start(); + } + + //new ConnectionBot(sessionId).run(); //this.executorService.execute(new ConnectionBot(sessionId)); } diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java index b837b219..67bb12ec 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java @@ -27,10 +27,10 @@ import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; -import org.apache.commons.codec.Charsets; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.junit.After; import org.junit.Before; @@ -40,6 +40,7 @@ import org.junit.runners.MethodSorters; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpStatus; import org.springframework.test.context.jdbc.Sql; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -47,12 +48,15 @@ import org.springframework.util.StreamUtils; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.API.BatchActionType; import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.client.ClientCredentials; +import ch.ethz.seb.sebserver.gbl.model.BatchAction; import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.Domain.BATCH_ACTION; import ch.ethz.seb.sebserver.gbl.model.Domain.SEB_CLIENT_CONFIGURATION; import ch.ethz.seb.sebserver.gbl.model.EntityDependency; import ch.ethz.seb.sebserver.gbl.model.EntityKey; @@ -70,6 +74,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold; import ch.ethz.seb.sebserver.gbl.model.exam.IndicatorTemplate; +import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringRoomConnection; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType; @@ -96,7 +101,9 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig; import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute; import ch.ethz.seb.sebserver.gbl.model.sebconfig.View; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; +import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; +import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.ExportType; import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction; import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType; import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification; @@ -104,6 +111,7 @@ import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent; import ch.ethz.seb.sebserver.gbl.model.session.IndicatorValue; import ch.ethz.seb.sebserver.gbl.model.session.MonitoringFullPageData; import ch.ethz.seb.sebserver.gbl.model.session.MonitoringSEBConnectionData; +import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom; import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserRole; @@ -114,7 +122,11 @@ import ch.ethz.seb.sebserver.gui.service.examconfig.impl.AttributeMapping; import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ExamConfigurationServiceImpl; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestServiceImpl; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.batch.DoBatchAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.batch.GetBatchAction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.batch.GetBatchActionPage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.ActivateSEBRestriction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.ArchiveExam; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckExamConsistency; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckExamImported; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckSEBRestriction; @@ -133,6 +145,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamConfi import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamDependencies; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamNames; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplate; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplatePage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplates; @@ -141,7 +154,6 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicator import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorTemplate; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorTemplatePage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetSEBRestrictionSettings; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewExamConfigMapping; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewExamTemplate; @@ -149,10 +161,10 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewIndicator import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewIndicatorTemplate; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamConfigMapping; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamProctoringSettings; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamTemplate; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveIndicator; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveIndicatorTemplate; -import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveProctoringSettings; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveSEBRestriction; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.ActivateInstitution; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution; @@ -165,7 +177,9 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSe import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.NewLmsSetup; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.SaveLmsSetup; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.TestLmsSetup; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.DeleteAllClientEvents; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.DeleteAllUserLogs; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.ExportSEBClientLogs; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetUserLogNames; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetUserLogPage; @@ -214,15 +228,27 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.Sa import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigHistory; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigTableValues; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigValue; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.CloseProctoringRoom; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.ConfirmPendingClientNotification; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.DisableClientConnection; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnection; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionDataList; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetCollectingRoomConnections; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetCollectingRooms; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamClientConnection; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamClientConnectionPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamPage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetMonitoringFullPageData; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetPendingClientNotifications; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetProctorRoomConnection; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetRunningExamPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetTownhallRoom; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.IsTownhallRoomAvailable; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.NotifyProctoringRoomOpened; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.OpenTownhallRoom; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.PropagateInstruction; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.SendProctoringReconfigurationAttributes; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.ActivateUserAccount; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.ChangePassword; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.DeleteUserAccount; @@ -232,35 +258,32 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUs import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.NewUserAccount; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.RegisterNewUser; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.SaveUserAccount; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringRoomService; @FixMethodOrder(MethodSorters.NAME_ASCENDING) - public class UseCasesIntegrationTest extends GuiIntegrationTest { @Autowired private Cryptor cryptor; - @Autowired - private LmsAPIService lmsAPIService; @Before @Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql" }) public void init() { - this.lmsAPIService.cleanup(); + // Nothing } @After @Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql" }) public void cleanup() { - this.lmsAPIService.cleanup(); + // Nothing } @Test @Order(1) // ************************************* // Use Case 1: SEB Administrator creates a new institution and activate this new institution - public void testUsecase01() { final RestServiceImpl restService = createRestServiceForUser( "admin", @@ -789,6 +812,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { // - Check if there are some quizzes from previous LMS Setup // - Import a quiz as Exam // - get exam page and check the exam is there + // - get exam page with none native sort attribute to test this // - edit exam property and save again public void testUsecase07_ImportExam() { final RestServiceImpl restService = createRestServiceForUser( @@ -835,14 +859,6 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { //assertEquals(Long.valueOf(1), quizData.lmsSetupId); assertEquals(Long.valueOf(4), quizData.institutionId); - // check imported - final Result> checkCall = restService.getBuilder(CheckExamImported.class) - .withURIVariable(API.PARAM_MODEL_ID, quizData.getModelId()) - .call(); - assertFalse(checkCall.hasError()); - final Collection importCheck = checkCall.getOrThrow(); - assertTrue(importCheck.isEmpty()); // not imported at all - // import quiz as exam final Result newExamResult = restService .getBuilder(ImportAsExam.class) @@ -865,15 +881,14 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { newExam.institutionId, newExam.lmsSetupId, newExam.externalId, + true, newExam.name, - newExam.description, newExam.startTime, newExam.endTime, - newExam.startURL, ExamType.MANAGED, null, Utils.immutableCollectionOf(userId), - ExamStatus.RUNNING, + null, false, null, true, @@ -905,6 +920,13 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .filter(exam -> exam.name.equals(newExam.name)) .findFirst().isPresent()); + final Result> examsSorted = restService + .getBuilder(GetExamPage.class) + .withQueryParam(Page.ATTR_SORT, LmsSetup.FILTER_ATTR_LMS_SETUP) + .call(); + + assertNotNull(examsSorted); + assertFalse(examsSorted.hasError()); } @Test @@ -1555,7 +1577,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .withURIVariable(API.PARAM_MODEL_ID, configurationNode.getModelId()) .withResponseExtractor(response -> { final InputStream input = response.getBody(); - final String xmlString = StreamUtils.copyToString(input, Charsets.UTF_8); + final String xmlString = StreamUtils.copyToString(input, java.nio.charset.StandardCharsets.UTF_8); assertNotNull(xmlString); for (final ConfigurationAttribute attribute : attributes) { if (attribute.name.contains(".") || attribute.name.equals("kioskMode")) { @@ -1696,7 +1718,9 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { config.description, ConfigurationType.EXAM_CONFIG, config.owner, - ConfigurationStatus.READY_TO_USE); + ConfigurationStatus.READY_TO_USE, + DateTime.now(), + config.owner); final ConfigurationNode savedConfig = restService .getBuilder(SaveExamConfig.class) @@ -2078,7 +2102,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .withURIVariable(API.PARAM_PARENT_MODEL_ID, String.valueOf(examConfigurationMap.examId)) .withResponseExtractor(response -> { final InputStream input = response.getBody(); - final String xmlString = StreamUtils.copyToString(input, Charsets.UTF_8); + final String xmlString = StreamUtils.copyToString(input, java.nio.charset.StandardCharsets.UTF_8); assertNotNull(xmlString); // assertEquals( // "allowAudioCaptureallowBrowsingBackForwardallowDictationallowDictionaryLookupallowDisplayMirroringallowDownUploadsallowedDisplayBuiltinallowedDisplaysMaxNumber1allowFlashFullscreenallowiOSBetaVersionNumber0allowiOSVersionNumberMajor9allowiOSVersionNumberMinor3allowiOSVersionNumberPatch5allowPDFPlugInallowPreferencesWindowallowQuitallowScreenSharingallowSiriallowSpellCheckallowSpellCheckDictionaryda-DKen-AUen-GBen-USes-ESfr-FRpt-PTsv-SEsv-FIallowSwitchToApplicationsallowUserAppFolderInstallallowUserSwitchingallowVideoCaptureallowVirtualMachineallowWlanaudioControlEnabledaudioMuteaudioSetVolumeLevelaudioVolumeLevel25blacklistURLFilterblockPopUpWindowsbrowserMessagingPingTime120000browserMessagingSocketws://localhost:8706browserScreenKeyboardbrowserURLSaltbrowserUserAgentbrowserUserAgentiOS0browserUserAgentiOSCustombrowserUserAgentMac0browserUserAgentMacCustombrowserUserAgentWinDesktopMode0browserUserAgentWinDesktopModeCustombrowserUserAgentWinTouchMode0browserUserAgentWinTouchModeCustombrowserUserAgentWinTouchModeIPadMozilla/5.0 (iPad; CPU OS 12_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Mobile/15E148 Safari/604.1browserViewMode0browserWindowAllowReloadbrowserWindowShowURL0browserWindowTitleSuffixchooseFileToUploadPolicy0createNewDesktopdetectStoppedProcessdownloadAndOpenSebConfigdownloadDirectoryOSXdownloadDirectoryWindownloadPDFFilesenableAltEscenableAltF4enableAltMouseWheelenableAltTabenableAppSwitcherCheckenableBrowserWindowToolbarenableCtrlEscenableDrawingEditorenableEscenableF1enableF10enableF11enableF12enableF2enableF3enableF4enableF5enableF6enableF7enableF8enableF9enableJavaenableJavaScriptenableLoggingenablePlugInsenablePrintScreenenablePrivateClipboardenableRightMouseenableSebBrowserenableStartMenuenableTouchExitenableZoomPageenableZoomTextexamSessionClearCookiesOnEndexamSessionClearCookiesOnStartexitKey12exitKey210exitKey35forceAppFolderInstallhashedAdminPasswordhashedQuitPasswordhideBrowserWindowToolbarhookKeysignoreExitKeysinsideSebEnableChangeAPasswordinsideSebEnableEaseOfAccessinsideSebEnableLockThisComputerinsideSebEnableLogOffinsideSebEnableNetworkConnectionSelectorinsideSebEnableShutDowninsideSebEnableStartTaskManagerinsideSebEnableSwitchUserinsideSebEnableVmWareClientShadekillExplorerShelllockOnMessageSocketCloselogDirectoryOSXlogDirectoryWinlogLevel1mainBrowserWindowHeight100%mainBrowserWindowPositioning1mainBrowserWindowWidth100%minMacOSVersion0mobileAllowPictureInPictureMediaPlaybackmobileAllowQRCodeConfigmobileAllowSingleAppModemobileEnableASAMmobileEnableGuidedAccessLinkTransformmobilePreventAutoLockmobileShowSettingsmobileStatusBarAppearance1mobileStatusBarAppearanceExtended1monitorProcessesnewBrowserWindowAllowReloadnewBrowserWindowByLinkBlockForeignnewBrowserWindowByLinkHeight100%newBrowserWindowByLinkPolicy2newBrowserWindowByLinkPositioning2newBrowserWindowByLinkWidth100%newBrowserWindowByScriptBlockForeignnewBrowserWindowByScriptPolicy2newBrowserWindowNavigationnewBrowserWindowShowReloadWarningnewBrowserWindowShowURL1openDownloadsoriginatorVersionSEB_Server_0.3.0permittedProcessesactiveallowUserToChooseAppargumentsautostartdescriptionexecutablefirefox.exeiconInTaskbaridentifierFirefoxoriginalNamefirefox.exeos1path../xulrunner/runInBackgroundstrongKilltitleSEBpinEmbeddedCertificatesprohibitedProcessesactivecurrentUserdescriptionexecutableRiotidentifieroriginalNameRiotos1strongKilluseractivecurrentUserdescriptionexecutableseamonkeyidentifieroriginalNameseamonkeyos1strongKilluseractivecurrentUserdescriptionexecutableDiscordidentifieroriginalNameDiscordos1strongKilluseractivecurrentUserdescriptionexecutableSlackidentifieroriginalNameSlackos1strongKilluseractivecurrentUserdescriptionexecutableTeamsidentifieroriginalNameTeamsos1strongKilluseractivecurrentUserdescriptionexecutableCamRecorderidentifieroriginalNameCamRecorderos1strongKilluseractivecurrentUserdescriptionexecutablejoin.meidentifieroriginalNamejoin.meos1strongKilluseractivecurrentUserdescriptionexecutableRPCSuiteidentifieroriginalNameRPCSuiteos1strongKilluseractivecurrentUserdescriptionexecutableRPCServiceidentifieroriginalNameRPCServiceos1strongKilluseractivecurrentUserdescriptionexecutableRemotePCDesktopidentifieroriginalNameRemotePCDesktopos1strongKilluseractivecurrentUserdescriptionexecutablebeamyourscreen-hostidentifieroriginalNamebeamyourscreen-hostos1strongKilluseractivecurrentUserdescriptionexecutableAeroAdminidentifieroriginalNameAeroAdminos1strongKilluseractivecurrentUserdescriptionexecutableMikogo-hostidentifieroriginalNameMikogo-hostos1strongKilluseractivecurrentUserdescriptionexecutablechromotingidentifieroriginalNamechromotingos1strongKilluseractivecurrentUserdescriptionexecutablevncserveruiidentifieroriginalNamevncserveruios1strongKilluseractivecurrentUserdescriptionexecutablevncvieweridentifieroriginalNamevncvieweros1strongKilluseractivecurrentUserdescriptionexecutablevncserveridentifieroriginalNamevncserveros1strongKilluseractivecurrentUserdescriptionexecutableTeamVieweridentifieroriginalNameTeamVieweros1strongKilluseractivecurrentUserdescriptionexecutableGotoMeetingWinStoreidentifieroriginalNameGotoMeetingWinStoreos1strongKilluseractivecurrentUserdescriptionexecutableg2mcomm.exeidentifieroriginalNameg2mcomm.exeos1strongKilluseractivecurrentUserdescriptionexecutableSkypeHostidentifieroriginalNameSkypeHostos1strongKilluseractivecurrentUserdescriptionexecutableSkypeidentifieroriginalNameSkypeos1strongKilluserproxiesAutoConfigurationEnabledAutoConfigurationJavaScriptAutoConfigurationURLAutoDiscoveryEnabledExceptionsListExcludeSimpleHostnamesFTPEnableFTPPassiveFTPPasswordFTPPort21FTPProxyFTPRequiresPasswordFTPUsernameHTTPEnableHTTPPasswordHTTPPort80HTTPProxyHTTPRequiresPasswordHTTPSEnableHTTPSPasswordHTTPSPort443HTTPSProxyHTTPSRequiresPasswordHTTPSUsernameHTTPUsernameRTSPEnableRTSPPasswordRTSPPort554RTSPProxyRTSPRequiresPasswordRTSPUsernameSOCKSEnableSOCKSPasswordSOCKSPort1080SOCKSProxySOCKSRequiresPasswordSOCKSUsernameproxySettingsPolicy0quitURLquitURLConfirmremoveBrowserProfileremoveLocalStoragerestartExamPasswordProtectedrestartExamTextrestartExamURLrestartExamUseStartURLsebConfigPurpose0sebServicePolicy2sendBrowserExamKeyshowBackToStartButtonshowInputLanguageshowMenuBarshowNavigationButtonsshowReloadButtonshowReloadWarningshowScanQRCodeButtonshowSettingsInAppshowTaskBarshowTimestartResourcetaskBarHeight40touchOptimizedURLFilterEnableURLFilterEnableContentFilterURLFilterMessage0URLFilterRulesuseAsymmetricOnlyEncryptionwhitelistURLFilterzoomMode0", @@ -2108,11 +2132,16 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { new GetClientConnection(), new GetMonitoringFullPageData(), new GetExtendedClientEventPage(), + new ExportSEBClientLogs(), + new DeleteAllClientEvents(), new DisableClientConnection(), new PropagateInstruction(), new GetClientConnectionPage(), new GetPendingClientNotifications(), - new ConfirmPendingClientNotification()); + new ConfirmPendingClientNotification(), + new GetFinishedExamPage(), + new GetFinishedExamClientConnectionPage(), + new GetFinishedExamClientConnection()); final RestServiceImpl adminRestService = createRestServiceForUser( "TestInstAdmin", @@ -2121,7 +2150,8 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { new ActivateClientConfig(), new GetClientConfigPage(), new GetIndicatorPage(), - new GetIndicators()); + new GetIndicators(), + new DeleteAllClientEvents()); // get running exams final Result> runningExamsCall = restService.getBuilder(GetRunningExamPage.class) @@ -2147,7 +2177,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { assertTrue(connections.isEmpty()); // get MonitoringFullPageData - final Result fullPageData = restService.getBuilder(GetMonitoringFullPageData.class) + Result fullPageData = restService.getBuilder(GetMonitoringFullPageData.class) .withURIVariable(API.PARAM_PARENT_MODEL_ID, exam.getModelId()) .call(); assertNotNull(fullPageData); @@ -2160,7 +2190,8 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .collect(Collectors.toList()).toString()); // get active client config's credentials - final Result> cconfigs = adminRestService.getBuilder(GetClientConfigPage.class) + final Result> cconfigs = adminRestService + .getBuilder(GetClientConfigPage.class) .call(); assertNotNull(cconfigs); assertFalse(cconfigs.hasError()); @@ -2169,7 +2200,8 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { final SEBClientConfig clientConfig = ccPage.content.get(0); assertTrue(clientConfig.isActive()); - final ClientCredentials credentials = this.sebClientConfigDAO.getSEBClientCredentials(clientConfig.getModelId()) + final ClientCredentials credentials = this.sebClientConfigDAO + .getSEBClientCredentials(clientConfig.getModelId()) .getOrThrow(); adminRestService.getBuilder(ActivateClientConfig.class) @@ -2182,8 +2214,9 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { credentials.clientIdAsString(), this.cryptor.decrypt(credentials.secret).getOrThrow().toString(), exam.getModelId(), - String.valueOf(exam.institutionId)); - Thread.sleep(1000); + String.valueOf(exam.institutionId), + true); + //Thread.sleep(1000); // send get connections connectionsCall = @@ -2199,6 +2232,22 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { iterator.next(); final ClientConnectionData con = iterator.next(); + fullPageData = restService.getBuilder(GetMonitoringFullPageData.class) + .withURIVariable(API.PARAM_PARENT_MODEL_ID, exam.getModelId()) + .withHeader(API.EXAM_MONITORING_STATE_FILTER, ConnectionStatus.DISABLED.name()) + .call(); + assertNotNull(fullPageData); + assertFalse(fullPageData.hasError()); + + fullPageData = restService.getBuilder(GetMonitoringFullPageData.class) + .withURIVariable(API.PARAM_PARENT_MODEL_ID, exam.getModelId()) + .withHeader( + API.EXAM_MONITORING_STATE_FILTER, + ConnectionStatus.DISABLED.name() + "," + ConnectionStatus.ACTIVE.name()) + .call(); + assertNotNull(fullPageData); + assertFalse(fullPageData.hasError()); + // get single client connection final Result ccCall = restService.getBuilder(GetClientConnection.class) .withURIVariable(API.PARAM_MODEL_ID, con.clientConnection.getModelId()) @@ -2206,8 +2255,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { assertFalse(ccCall.hasError()); final ClientConnection clientConnection = ccCall.get(); - assertEquals("1", clientConnection.examId.toString()); - //assertEquals("", clientConnection.status.name()); + assertTrue(clientConnection.userSessionId.contains("connection")); // get notification final Result> notificationsCall = @@ -2246,7 +2294,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { assertNotNull(instructionCall); assertFalse(instructionCall.hasError()); - Thread.sleep(1000); + //Thread.sleep(1000); } catch (final Exception e) { fail(e.getMessage()); } @@ -2298,7 +2346,8 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { assertEquals("DISABLED", conData.clientConnection.status.name()); // get client logs - final Result> clientLogPage = restService.getBuilder(GetExtendedClientEventPage.class) + final Result> clientLogPage = restService + .getBuilder(GetExtendedClientEventPage.class) .call(); assertNotNull(clientLogPage); @@ -2308,6 +2357,32 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { final ExtendedClientEvent extendedClientEvent = clientLogs.content.get(0); assertNotNull(extendedClientEvent); + // export client logs + restService + .getBuilder(ExportSEBClientLogs.class) + .withQueryParam(API.SEB_CLIENT_EVENT_EXPORT_TYPE, ExportType.CSV.name()) + .withQueryParam(API.SEB_CLIENT_EVENT_EXPORT_INCLUDE_EXAMS, "true") + .withResponseExtractor(response -> { + final HttpStatus statusCode = response.getStatusCode(); + assertEquals("200 OK", statusCode.toString()); + final String csvExport = IOUtils.toString(response.getBody()); + assertTrue(StringUtils.isNotBlank(csvExport)); + return true; + }) + .call(); + + // delete client logs + final Result report = adminRestService + .getBuilder(DeleteAllClientEvents.class) + .withFormParam( + API.PARAM_MODEL_ID_LIST, + StringUtils.join( + clientLogs.content.stream().map(e -> e.getModelId()).collect(Collectors.toList()), ",")) + .call(); + + assertNotNull(report); + assertFalse(report.hasError()); + // get client connection page Result> connectionPageRes = restService .getBuilder(GetClientConnectionPage.class) @@ -2318,15 +2393,65 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { assertNotNull(connectionPage); assertFalse(connectionPage.isEmpty()); + connectionPageRes = restService + .getBuilder(GetClientConnectionPage.class) + .withQueryParam(ClientConnection.FILTER_ATTR_INFO, "") + .withQueryParam(Page.ATTR_SORT, Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID) + .call(); + + assertNotNull(connectionPageRes); + connectionPage = connectionPageRes.get(); + assertNotNull(connectionPage); + assertFalse(connectionPage.isEmpty()); + connectionPageRes = restService .getBuilder(GetClientConnectionPage.class) .withQueryParam(ClientConnection.FILTER_ATTR_INFO, "ghfhrthjrt") + .withQueryParam(Page.ATTR_SORT, Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID) .call(); assertNotNull(connectionPageRes); connectionPage = connectionPageRes.get(); assertNotNull(connectionPage); assertTrue(connectionPage.isEmpty()); + + Result> connectionDatacall = restService + .getBuilder(GetFinishedExamClientConnectionPage.class) + .withURIVariable(API.PARAM_PARENT_MODEL_ID, exam.getModelId()) + .call(); + assertNotNull(connectionDatacall); + assertFalse(connectionDatacall.hasError()); + Page ccDataPage = connectionDatacall.get(); + assertNotNull(ccDataPage); + assertFalse(ccDataPage.content.isEmpty()); + final ClientConnectionData clientConnectionData = ccDataPage.content.get(0); + assertNotNull(clientConnectionData); + assertEquals("DISABLED", clientConnectionData.clientConnection.status.toString()); + + connectionDatacall = restService + .getBuilder(GetFinishedExamClientConnectionPage.class) + .withURIVariable(API.PARAM_PARENT_MODEL_ID, exam.getModelId()) + .withQueryParam(ClientConnection.FILTER_ATTR_INFO, "test") + .call(); + assertNotNull(connectionDatacall); + assertFalse(connectionDatacall.hasError()); + ccDataPage = connectionDatacall.get(); + assertNotNull(ccDataPage); + assertTrue(ccDataPage.content.isEmpty()); + + final Result ccDataCall = restService + .getBuilder(GetFinishedExamClientConnection.class) + .withURIVariable(API.PARAM_MODEL_ID, clientConnectionData.getModelId()) + .call(); + + assertNotNull(ccDataCall); + assertFalse(ccDataCall.hasError()); + final ClientConnectionData clientConnectionData2 = ccDataCall.get(); + assertNotNull(clientConnectionData2); + assertEquals( + clientConnectionData2.clientConnection.connectionToken, + clientConnectionData.clientConnection.connectionToken); + } @Test @@ -2363,18 +2488,18 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .collect(Collectors.toList()); assertEquals( - "[CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "EXAM, " - + "EXAM_CONFIGURATION_MAP, " + "[EXAM, " + "INDICATOR, " - + "INDICATOR]", + + "INDICATOR, " + + "EXAM_CONFIGURATION_MAP, " + + "CONFIGURATION_NODE, " + + "CONFIGURATION_NODE, " + + "CONFIGURATION_NODE, " + + "CONFIGURATION_NODE, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION]", dependencies.stream().map(dep -> dep.self.entityType).collect(Collectors.toList()).toString()); // check that the user is owner of all depending exams and configurations @@ -2412,14 +2537,14 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .collect(Collectors.toList()); assertEquals( - "[CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "EXAM, " - + "EXAM_CONFIGURATION_MAP, " + "[EXAM, " + "INDICATOR, " - + "INDICATOR]", + + "INDICATOR, " + + "EXAM_CONFIGURATION_MAP, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION]", dependencies.stream().map(dep -> dep.self.entityType).collect(Collectors.toList()).toString()); // only with configuration dependencies @@ -2434,11 +2559,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .collect(Collectors.toList()); assertEquals( - "[CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "EXAM_CONFIGURATION_MAP]", + "[EXAM_CONFIGURATION_MAP, CONFIGURATION_NODE, CONFIGURATION_NODE, CONFIGURATION_NODE, CONFIGURATION_NODE]", dependencies.stream().map(dep -> dep.self.entityType).collect(Collectors.toList()).toString()); // only with exam and configuration dependencies @@ -2454,18 +2575,18 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .collect(Collectors.toList()); assertEquals( - "[CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CLIENT_CONNECTION, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "CONFIGURATION_NODE, " - + "EXAM, " - + "EXAM_CONFIGURATION_MAP, " + "[EXAM, " + "INDICATOR, " - + "INDICATOR]", + + "INDICATOR, " + + "EXAM_CONFIGURATION_MAP, " + + "CONFIGURATION_NODE, " + + "CONFIGURATION_NODE, " + + "CONFIGURATION_NODE, " + + "CONFIGURATION_NODE, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION, " + + "CLIENT_CONNECTION]", dependencies.stream().map(dep -> dep.self.entityType).collect(Collectors.toList()).toString()); } @@ -3321,7 +3442,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { assertTrue(applyCall.hasError()); assertTrue(applyCall.getError() instanceof RestCallError); - assertTrue(applyCall.getError().getCause().getMessage().contains("SEB Restriction feature not available")); + assertTrue(applyCall.getError().toString().contains("SEB Restriction feature not available")); final Result deleteCall = restService .getBuilder(DeactivateSEBRestriction.class) @@ -3330,7 +3451,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { assertTrue(deleteCall.hasError()); assertTrue(deleteCall.getError() instanceof RestCallError); - assertTrue(deleteCall.getError().getCause().getMessage().contains("SEB Restriction feature not available")); + assertTrue(deleteCall.getError().toString().contains("SEB Restriction feature not available")); final Result chaptersCall = restService .getBuilder(GetCourseChapters.class) @@ -3339,7 +3460,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { assertTrue(chaptersCall.hasError()); assertTrue(chaptersCall.getError() instanceof RestCallError); - assertTrue(chaptersCall.getError().getCause().getMessage().contains("Course Chapter feature not supported")); + assertTrue(chaptersCall.getError().toString().contains("Course Chapter feature not supported")); } @@ -3351,15 +3472,15 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { final RestServiceImpl restService = createRestServiceForUser( "admin", "admin", - new GetProctoringSettings(), - new SaveProctoringSettings()); + new GetExamProctoringSettings(), + new SaveExamProctoringSettings()); final Exam exam = createTestExam("admin", "admin"); assertNotNull(exam); assertEquals("Demo Quiz 6 (MOCKUP)", exam.name); final ProctoringServiceSettings settings = restService - .getBuilder(GetProctoringSettings.class) + .getBuilder(GetExamProctoringSettings.class) .withURIVariable(API.PARAM_MODEL_ID, exam.getModelId()) .call() .getOrThrow(); @@ -3381,16 +3502,16 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { "sdkSecret", false); - final Result saveCall = restService - .getBuilder(SaveProctoringSettings.class) + final Result saveCall = restService + .getBuilder(SaveExamProctoringSettings.class) .withURIVariable(API.PARAM_MODEL_ID, exam.getModelId()) .withBody(newSettings) .call(); if (!saveCall.hasError()) { assertFalse(saveCall.hasError()); - final Exam exam2 = saveCall.get(); - assertEquals(exam2.id, exam.id); + final ProctoringServiceSettings settings2 = saveCall.get(); + assertEquals(settings2.examId, exam.id); } } @@ -3444,4 +3565,466 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { .getOrThrow(); } + @Test + @Order(27) + // ************************************* + // Use Case 27: Login as admin and set exam proctoring settings for Jtisi + // - Get Exam (running) + // - Set Proctoring settings for exam + // - Check settings for exam + public void testUsecase27_SetProctoringSettingsJitsiForExam() throws IOException { + final RestServiceImpl restService = createRestServiceForUser( + "admin", + "admin", + new GetExamPage(), + new GetExamProctoringSettings(), + new SaveExamProctoringSettings(), + new IsTownhallRoomAvailable(), + new GetCollectingRooms()); + + // get exam + final Result> exams = restService + .getBuilder(GetExamPage.class) + .call(); + + assertNotNull(exams); + assertFalse(exams.hasError()); + final Page examPage = exams.get(); + assertFalse(examPage.isEmpty()); + + final Exam runningExam = examPage.content + .stream() + .filter(exam -> exam.status == ExamStatus.RUNNING) + .findFirst() + .orElse(null); + + assertNotNull(runningExam); + assertTrue(runningExam.status == ExamStatus.RUNNING); + + final Result pSettings = restService + .getBuilder(GetExamProctoringSettings.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call(); + + assertNotNull(pSettings); + assertFalse(pSettings.hasError()); + ProctoringServiceSettings proctoringServiceSettings = pSettings.get(); + assertFalse(proctoringServiceSettings.enableProctoring); + assertNull(proctoringServiceSettings.serverURL); + + // set proctoring settings + final ProctoringServiceSettings newProctoringServiceSettings = new ProctoringServiceSettings( + runningExam.id, + true, + ProctoringServerType.JITSI_MEET, + "https://test.proc/service", + 2, + EnumSet.allOf(ProctoringFeature.class), + true, + "appKey", "appSecret", + "sdkKey", "sdkSecret", + false); + + final Result newProcSettings = restService + .getBuilder(SaveExamProctoringSettings.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .withBody(newProctoringServiceSettings) + .call(); + + assertNotNull(newProcSettings); + assertFalse(newProcSettings.hasError()); + + proctoringServiceSettings = restService + .getBuilder(GetExamProctoringSettings.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + + assertTrue(proctoringServiceSettings.enableProctoring); + assertEquals("https://test.proc/service", proctoringServiceSettings.serverURL); + + final String twonhallRoom = restService + .getBuilder(IsTownhallRoomAvailable.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + + assertEquals("true", twonhallRoom); + + final Collection collectingRooms = restService + .getBuilder(GetCollectingRooms.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + + assertNotNull(collectingRooms); + assertTrue(collectingRooms.isEmpty()); + } + + @Autowired + private ExamProctoringRoomService examProcotringRoomService; + @Autowired + private ClientConnectionDAO clientConnectionDAO; + + @Test + @Order(28) + // ************************************* + // Use Case 28: Login as admin and connect with SEBs to running exam with procotring enabled + // - Get Exam (running) + // - start some SEB clients connecting to running exam + // - Check collecting rooms created + public void testUsecase28_TestExamProctoring() throws IOException { + + final RestServiceImpl restService = createRestServiceForUser( + "admin", + "admin", + new GetExamPage(), + new GetExamProctoringSettings(), + new SaveExamProctoringSettings(), + new IsTownhallRoomAvailable(), + new GetCollectingRooms(), + new GetClientConfigPage(), + new ActivateClientConfig(), + new NewClientConfig(), + new GetClientConfig(), + new GetProctorRoomConnection(), + new GetCollectingRoomConnections(), + new NotifyProctoringRoomOpened(), + new SendProctoringReconfigurationAttributes(), + new GetTownhallRoom(), + new OpenTownhallRoom(), + new CloseProctoringRoom()); + + // get exam + final Result> exams = restService + .getBuilder(GetExamPage.class) + .call(); + + assertNotNull(exams); + assertFalse(exams.hasError()); + final Page examPage = exams.get(); + assertFalse(examPage.isEmpty()); + + final Exam runningExam = examPage.content + .stream() + .filter(exam -> exam.status == ExamStatus.RUNNING) + .findFirst() + .orElse(null); + + assertNotNull(runningExam); + assertTrue(runningExam.status == ExamStatus.RUNNING); + + final Result pSettings = restService + .getBuilder(GetExamProctoringSettings.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call(); + + assertNotNull(pSettings); + assertFalse(pSettings.hasError()); + final ProctoringServiceSettings proctoringServiceSettings = pSettings.get(); + assertTrue(proctoringServiceSettings.enableProctoring); + assertEquals("https://test.proc/service", proctoringServiceSettings.serverURL); + + // start some SEB connections for this exam + + // create SEB Client Config without password protection + Result newConfigResponse = restService + .getBuilder(NewClientConfig.class) + .withFormParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "No Password Protection") + .withFormParam(SEBClientConfig.ATTR_FALLBACK, Constants.TRUE_STRING) + .withFormParam(SEBClientConfig.ATTR_FALLBACK_START_URL, "http://fallback.com/fallback") + .withFormParam(SEBClientConfig.ATTR_FALLBACK_TIMEOUT, "100") + .withFormParam(SEBClientConfig.ATTR_FALLBACK_ATTEMPTS, "5") + .withFormParam(SEBClientConfig.ATTR_FALLBACK_ATTEMPT_INTERVAL, "5") + .withFormParam(SEBClientConfig.ATTR_CONFIG_PURPOSE, SEBClientConfig.ConfigPurpose.START_EXAM.name()) + .call(); + + assertNotNull(newConfigResponse); + assertFalse(newConfigResponse.hasError()); + final SEBClientConfig sebClientConfig = newConfigResponse.get(); + assertEquals("No Password Protection", sebClientConfig.name); + assertFalse(sebClientConfig.isActive()); + assertEquals("http://fallback.com/fallback", sebClientConfig.fallbackStartURL); + + // activate the new Client Configuration + restService + .getBuilder(ActivateClientConfig.class) + .withURIVariable(API.PARAM_MODEL_ID, sebClientConfig.getModelId()) + .call(); + + newConfigResponse = restService.getBuilder(GetClientConfig.class) + .withURIVariable(API.PARAM_MODEL_ID, sebClientConfig.getModelId()) + .call(); + + final SEBClientConfig clientConfig = newConfigResponse.get(); + assertTrue(clientConfig.isActive()); + final ClientCredentials credentials = this.sebClientConfigDAO + .getSEBClientCredentials(clientConfig.getModelId()) + .getOrThrow(); + + assertTrue(clientConfig.isActive()); + + // simulate a SEB connection + try { + new SEBClientBot( + credentials.clientIdAsString(), + this.cryptor.decrypt(credentials.secret).getOrThrow().toString(), + runningExam.getModelId(), + String.valueOf(runningExam.institutionId), + false); + + Thread.sleep(1000); + + this.examProcotringRoomService.updateProctoringCollectingRooms(); + + // check collecting room was created + final Collection collectingRooms = restService + .getBuilder(GetCollectingRooms.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + + assertNotNull(collectingRooms); + assertFalse(collectingRooms.isEmpty()); + // Two rooms a two people for four connections + assertEquals(2, collectingRooms.size()); + final RemoteProctoringRoom room1 = collectingRooms.iterator().next(); + assertEquals(2, room1.roomSize.intValue()); + assertFalse(room1.townhallRoom); + + final ProctoringRoomConnection proctoringRoomConnection = restService + .getBuilder(GetProctorRoomConnection.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .withQueryParam(ProctoringRoomConnection.ATTR_ROOM_NAME, room1.name) + .call() + .get(); + + assertNotNull(proctoringRoomConnection); + assertEquals(room1.name, proctoringRoomConnection.roomName); + assertNotNull(proctoringRoomConnection.accessToken); + + // notify room open + restService + .getBuilder(NotifyProctoringRoomOpened.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .withQueryParam(ProctoringRoomConnection.ATTR_ROOM_NAME, room1.name) + .call() + .get(); + + // reconfigure clients in room + restService + .getBuilder(SendProctoringReconfigurationAttributes.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .withQueryParam(Domain.REMOTE_PROCTORING_ROOM.ATTR_ID, room1.name) + .withQueryParam(API.EXAM_PROCTORING_ATTR_RECEIVE_AUDIO, "true") + .withQueryParam(API.EXAM_PROCTORING_ATTR_RECEIVE_VIDEO, "true") + .withQueryParam(API.EXAM_PROCTORING_ATTR_ALLOW_CHAT, "true") + .call() + .get(); + + final Collection collection = restService + .getBuilder(GetCollectingRoomConnections.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .withQueryParam(Domain.REMOTE_PROCTORING_ROOM.ATTR_ID, room1.name) + .call() + .get(); + + assertNotNull(collection); + assertFalse(collection.isEmpty()); + assertEquals(2, collection.size()); + final ClientConnection connection = collection.iterator().next(); + assertEquals(runningExam.id, connection.examId); + // this is because the Json model do not contian certain attributes due to performance + assertNull(connection.remoteProctoringRoomId); + // we can geht the room number by getting it directyl from the record + final ClientConnection clientConnection = this.clientConnectionDAO.byPK(connection.id).get(); + assertNotNull(clientConnection.remoteProctoringRoomId); + + // get and open townhall + final String townhallActive = restService + .getBuilder(IsTownhallRoomAvailable.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + + assertEquals("true", townhallActive); + + // check no Townhallroom yet + RemoteProctoringRoom townhallRoom = restService + .getBuilder(GetTownhallRoom.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + assertEquals(RemoteProctoringRoom.NULL_ROOM, townhallRoom); + + // open townhall room + final ProctoringRoomConnection townhallRoomConntection = restService + .getBuilder(OpenTownhallRoom.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + + assertNotNull(townhallRoomConntection); + + // check Townhallroom is available yet + townhallRoom = restService + .getBuilder(GetTownhallRoom.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + assertTrue(townhallRoom.townhallRoom); + assertEquals(townhallRoom.name, townhallRoomConntection.roomName); + + // close townhall room + restService + .getBuilder(CloseProctoringRoom.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .withQueryParam(ProctoringRoomConnection.ATTR_ROOM_NAME, townhallRoom.name) + .call() + .get(); + + townhallRoom = restService + .getBuilder(GetTownhallRoom.class) + .withURIVariable(API.PARAM_MODEL_ID, runningExam.getModelId()) + .call() + .get(); + assertEquals(RemoteProctoringRoom.NULL_ROOM, townhallRoom); + + Thread.sleep(5000); + + } catch (final Exception e) { + fail(e.getMessage()); + } + } + + @Test + @Order(29) + // ************************************* + // Use Case 29: Login as admin and create some batch actions + // - Get Exam (running) + // - start some SEB clients connecting to running exam + // - Check collecting rooms created + public void testUsecase29_TestBatchAction() throws IOException, InterruptedException { + final RestServiceImpl restService = createRestServiceForUser( + "admin", + "admin", + new DoBatchAction(), + new GetBatchAction(), + new GetBatchActionPage(), + new GetExamConfigNodePage()); + + final ConfigurationNode config = restService + .getBuilder(GetExamConfigNodePage.class) + .call() + .getOrThrow().content + .get(0); + assertNotNull(config); + assertEquals("READY_TO_USE", config.status.toString()); + + // apply batch action + final Result doBatchAction = restService + .getBuilder(DoBatchAction.class) + .withFormParam(Domain.BATCH_ACTION.ATTR_ACTION_TYPE, BatchActionType.EXAM_CONFIG_STATE_CHANGE.name()) + .withFormParam(BATCH_ACTION.ATTR_SOURCE_IDS, config.getModelId()) + .withFormParam(BatchAction.ACTION_ATTRIBUT_TARGET_STATE, ConfigurationStatus.CONSTRUCTION.name()) + .call(); + + assertNotNull(doBatchAction); + assertFalse(doBatchAction.hasError()); + final BatchAction batchAction = doBatchAction.get(); + assertNotNull(batchAction); + assertNotNull(batchAction.ownerId); + assertFalse(batchAction.isFinished()); + assertEquals("EXAM_CONFIG_STATE_CHANGE", batchAction.actionType.name()); + + Thread.sleep(1000); + + final BatchAction savedBatchAction = restService + .getBuilder(GetBatchAction.class) + .withURIVariable(API.PARAM_MODEL_ID, batchAction.getModelId()) + .call().get(); + + assertNotNull(savedBatchAction); + assertNotNull(savedBatchAction.ownerId); + assertTrue(savedBatchAction.isFinished()); + assertEquals("EXAM_CONFIG_STATE_CHANGE", savedBatchAction.actionType.name()); + assertNotNull(savedBatchAction.processorId); + + final Page page = restService + .getBuilder(GetBatchActionPage.class) + .call().get(); + + assertNotNull(page); + assertFalse(page.content.isEmpty()); + } + + @Test + @Order(30) + // ************************************* + // Use Case 30: Login as admin and archive finished exam + // - Get Exam (finished), archive and check + public void testUsecase30_TestArchiveExam() throws IOException { + final RestServiceImpl restService = createRestServiceForUser( + "admin", + "admin", + new GetExamPage(), + new GetExam(), + new ArchiveExam()); + + final Page finishedExams = restService + .getBuilder(GetExamPage.class) + .withQueryParam(Exam.FILTER_ATTR_STATUS, ExamStatus.FINISHED.name()) + .call() + .get(); + + assertNotNull(finishedExams); + assertFalse(finishedExams.content.isEmpty()); + final Exam exam = finishedExams.content.get(0); + assertEquals(ExamStatus.FINISHED, exam.status); + + final Result archiveCall = restService.getBuilder(ArchiveExam.class) + .withURIVariable(API.PARAM_MODEL_ID, exam.getModelId()) + .call(); + + assertNotNull(archiveCall); + assertFalse(archiveCall.hasError()); + final Exam exam2 = archiveCall.get(); + assertNotNull(exam2); + assertEquals(exam.id, exam2.id); + assertEquals(ExamStatus.ARCHIVED, exam2.status); + } + + @Test + @Order(31) + // ************************************* + // Use Case 31: Login as admin and archive finished exam + // - Get Exam (running), archive and check not possible + public void testUsecase31_TestArchiveRunningExam_NotPossible() throws IOException { + final RestServiceImpl restService = createRestServiceForUser( + "admin", + "admin", + new GetExamPage(), + new GetExam(), + new ArchiveExam()); + + final Page finishedExams = restService + .getBuilder(GetExamPage.class) + .withQueryParam(Exam.FILTER_ATTR_STATUS, ExamStatus.RUNNING.name()) + .call() + .get(); + + assertNotNull(finishedExams); + assertFalse(finishedExams.content.isEmpty()); + final Exam exam = finishedExams.content.get(0); + assertEquals(ExamStatus.RUNNING, exam.status); + + final Result archiveCall = restService.getBuilder(ArchiveExam.class) + .withURIVariable(API.PARAM_MODEL_ID, exam.getModelId()) + .call(); + + assertNotNull(archiveCall); + assertTrue(archiveCall.hasError()); + assertTrue(archiveCall.getError().getMessage().contains("Exam is in wrong status to archive")); + } + } diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ConfigurationAttributeAPITest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ConfigurationAttributeAPITest.java new file mode 100644 index 00000000..af5c0ad4 --- /dev/null +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ConfigurationAttributeAPITest.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.integration.api.admin; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletRequest; + +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityName; +import ch.ethz.seb.sebserver.gbl.model.Page; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; +import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.UserServiceImpl; +import ch.ethz.seb.sebserver.webservice.weblayer.api.ConfigurationAttributeController; + +@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" }) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class ConfigurationAttributeAPITest extends AdministrationAPIIntegrationTester { + + @Autowired + private ConfigurationAttributeController configurationAttributeController; + @Autowired + private UserServiceImpl userServiceImpl; + @Mock + private HttpServletRequest mockRequest; + + private final MultiValueMap params = new LinkedMultiValueMap<>(); + + @Before + public void init() { + this.userServiceImpl.setAuthenticationIfAbsent(new SEBServerUser( + -1L, + new UserInfo("user1", 1L, null, "admin", null, null, null, true, null, null, + EnumSet.allOf(UserRole.class).stream().map(r -> r.name()).collect(Collectors.toSet())), + null)); + Mockito.when(this.mockRequest.getQueryString()).thenReturn(""); + } + + @Test + @Order(1) + public void test1_GetPage() { + final Page page = this.configurationAttributeController.getPage( + 1L, 0, 100, null, + new LinkedMultiValueMap(), + this.mockRequest); + + assertNotNull(page); + assertFalse(page.content.isEmpty()); + assertEquals("100", String.valueOf(page.content.size())); + } + + @Test + @Order(2) + public void test2_GetNames() { + Collection names = this.configurationAttributeController.getNames( + 1L, + new LinkedMultiValueMap(), + this.mockRequest); + + assertNotNull(names); + assertFalse(names.isEmpty()); + assertEquals("241", String.valueOf(names.size())); + + this.params.clear(); + this.params.add(ConfigurationAttribute.FILTER_ATTR_TYPE, AttributeType.CHECKBOX.name()); + + names = this.configurationAttributeController.getNames( + 1L, + this.params, + this.mockRequest); + + assertNotNull(names); + assertFalse(names.isEmpty()); + assertEquals("139", String.valueOf(names.size())); + } + + @Test + @Order(3) + public void test3_GetSingle() { + final ConfigurationAttribute attr = this.configurationAttributeController.getBy("1"); + + assertNotNull(attr); + assertEquals("hashedAdminPassword", attr.name); + } + + @Test + @Order(4) + public void test4_GetList() { + List forIds = this.configurationAttributeController.getForIds("1,2"); + + assertNotNull(forIds); + assertEquals("2", String.valueOf(forIds.size())); + + forIds = this.configurationAttributeController.getForIds(null); + + assertNotNull(forIds); + assertEquals("241", String.valueOf(forIds.size())); + } + + @Test + @Order(5) + public void test5_CreateAndSaveAndDelete() { + this.params.clear(); + this.params.add(Domain.CONFIGURATION_ATTRIBUTE.ATTR_PARENT_ID, null); + this.params.add(Domain.CONFIGURATION_ATTRIBUTE.ATTR_NAME, "testAttribute"); + this.params.add(Domain.CONFIGURATION_ATTRIBUTE.ATTR_TYPE, AttributeType.CHECKBOX.name()); + this.params.add(Domain.CONFIGURATION_ATTRIBUTE.ATTR_RESOURCES, ""); + this.params.add(Domain.CONFIGURATION_ATTRIBUTE.ATTR_VALIDATOR, ""); + this.params.add(Domain.CONFIGURATION_ATTRIBUTE.ATTR_DEPENDENCIES, ""); + this.params.add(Domain.CONFIGURATION_ATTRIBUTE.ATTR_DEFAULT_VALUE, "true"); + + final ConfigurationAttribute create = this.configurationAttributeController.create( + this.params, + 1L, + this.mockRequest); + + assertNotNull(create); + assertNotNull(create.id); + assertEquals("testAttribute", create.name); + assertEquals("true", create.defaultValue); + + final ConfigurationAttribute savePut = this.configurationAttributeController.savePut(new ConfigurationAttribute( + create.id, + null, null, null, null, null, null, + "false")); + + assertNotNull(savePut); + assertNotNull(savePut.id); + assertEquals("testAttribute", savePut.name); + assertEquals("false", savePut.defaultValue); + + } + + @Test + @Order(6) + public void test6_NoDeletionSupport() { + + try { + this.configurationAttributeController.hardDeleteAll( + Arrays.asList("1,2,3"), + false, + null, + 1L); + fail("Error expected here"); + } catch (final Exception e) { + assertEquals( + "No bulk action support for: BulkAction [type=HARD_DELETE, sourceType=CONFIGURATION_ATTRIBUTE, sources=[]]", + e.getMessage()); + } + + try { + this.configurationAttributeController.hardDelete( + "1", + false, + null); + fail("Error expected here"); + } catch (final Exception e) { + assertEquals( + "No bulk action support for: BulkAction [type=HARD_DELETE, sourceType=CONFIGURATION_ATTRIBUTE, sources=[EntityName [entityType=CONFIGURATION_ATTRIBUTE, modelId=1, name=hashedAdminPassword]]]", + e.getMessage()); + } + + } + +} diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java index ae0b4590..3076b492 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java @@ -54,12 +54,10 @@ public class ExamAPITest extends AdministrationAPIIntegrationTester { exam.id, exam.institutionId, exam.lmsSetupId, - exam.externalId, + exam.externalId, true, exam.name, - exam.description, exam.startTime, exam.endTime, - exam.startURL, exam.type, exam.owner, Arrays.asList("user5"), @@ -85,12 +83,10 @@ public class ExamAPITest extends AdministrationAPIIntegrationTester { exam.id, exam.institutionId, exam.lmsSetupId, - exam.externalId, + exam.externalId, true, exam.name, - exam.description, exam.startTime, exam.endTime, - exam.startURL, exam.type, exam.owner, Arrays.asList("user2"), diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamProctoringRoomServiceTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamProctoringRoomServiceTest.java index 9c1874c6..9b2446c3 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamProctoringRoomServiceTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamProctoringRoomServiceTest.java @@ -12,6 +12,7 @@ import static org.junit.Assert.*; import java.util.Collection; +import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Order; import org.springframework.beans.factory.annotation.Autowired; @@ -24,7 +25,10 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringRoomService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; @@ -42,6 +46,16 @@ public class ExamProctoringRoomServiceTest extends AdministrationAPIIntegrationT private ExamAdminService examAdminService; @Autowired private ClientConnectionDAO clientConnectionDAO; + @Autowired + private ExamDAO examDAO; + @Autowired + private LmsAPIService lmsAPIService; + + @Before + public void init() { + final LmsAPITemplate lmsAPITemplate = this.lmsAPIService.getLmsAPITemplate(1L).getOrThrow(); + this.examDAO.updateQuizData(2L, lmsAPITemplate.getQuiz("quiz6").getOrThrow(), "testUpdate"); + } @Test @Order(1) @@ -63,7 +77,7 @@ public class ExamProctoringRoomServiceTest extends AdministrationAPIIntegrationT this.examAdminService.saveProctoringServiceSettings( 2L, new ProctoringServiceSettings( - 2L, true, ProctoringServerType.JITSI_MEET, "http://jitsi.ch", 1, null, false, + 2L, true, ProctoringServerType.JITSI_MEET, "", 1, null, false, "app-key", "app.secret", "sdk-key", "sdk.secret", false)); assertTrue(this.examAdminService.isProctoringEnabled(2L).get()); diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/OrientationAPITest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/OrientationAPITest.java new file mode 100644 index 00000000..e41fcffc --- /dev/null +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/OrientationAPITest.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.integration.api.admin; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.EnumSet; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletRequest; + +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.Page; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.TitleOrientation; +import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.UserServiceImpl; +import ch.ethz.seb.sebserver.webservice.weblayer.api.OrientationController; + +@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" }) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class OrientationAPITest extends AdministrationAPIIntegrationTester { + + @Autowired + private OrientationController orientationController; + @Autowired + private UserServiceImpl userServiceImpl; + @Mock + private HttpServletRequest mockRequest; + + private final MultiValueMap params = new LinkedMultiValueMap<>(); + + @Before + public void init() { + this.userServiceImpl.setAuthenticationIfAbsent(new SEBServerUser( + -1L, + new UserInfo("user1", 1L, null, "admin", null, null, null, true, null, null, + EnumSet.allOf(UserRole.class).stream().map(r -> r.name()).collect(Collectors.toSet())), + null)); + Mockito.when(this.mockRequest.getQueryString()).thenReturn(""); + } + + @Test + @Order(1) + public void test1_GetPage() { + final Page page = this.orientationController.getPage( + 1L, 0, 100, null, + new LinkedMultiValueMap(), + this.mockRequest); + + assertNotNull(page); + assertFalse(page.content.isEmpty()); + assertEquals("100", String.valueOf(page.content.size())); + } + + @Test + @Order(5) + public void test5_CreateAndSaveAndDelete() { + this.params.clear(); + this.params.add(Domain.ORIENTATION.ATTR_CONFIG_ATTRIBUTE_ID, "1"); + this.params.add(Domain.ORIENTATION.ATTR_GROUP_ID, "testAttribute"); + this.params.add(Domain.ORIENTATION.ATTR_HEIGHT, "1"); + this.params.add(Domain.ORIENTATION.ATTR_TEMPLATE_ID, "0"); + this.params.add(Domain.ORIENTATION.ATTR_TITLE, "LEFT"); + this.params.add(Domain.ORIENTATION.ATTR_VIEW_ID, "1"); + this.params.add(Domain.ORIENTATION.ATTR_WIDTH, "1"); + this.params.add(Domain.ORIENTATION.ATTR_X_POSITION, "1"); + this.params.add(Domain.ORIENTATION.ATTR_Y_POSITION, "1"); + this.params.add(Domain.ORIENTATION.TYPE_NAME, "testAttribute"); + + final Orientation create = this.orientationController.create( + this.params, + 1L, + this.mockRequest); + + assertNotNull(create); + assertNotNull(create.id); + assertEquals("testAttribute", create.groupId); + assertEquals(1, create.height); + assertEquals(1, create.width); + assertEquals(1, create.xPosition); + assertEquals(1, create.yPosition); + assertEquals(TitleOrientation.LEFT, create.title); + + final Orientation savePut = this.orientationController.savePut(new Orientation( + create.id, + null, null, null, null, null, null, null, null, + TitleOrientation.RIGHT)); + + assertNotNull(savePut); + assertNotNull(savePut.id); + assertEquals("testAttribute", savePut.groupId); + assertEquals(TitleOrientation.RIGHT, savePut.title); + + } + +} diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/ExamAPIAccessTokenRequestTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/ExamAPIAccessTokenRequestTest.java index f3f25d4d..b1747ccd 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/ExamAPIAccessTokenRequestTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/ExamAPIAccessTokenRequestTest.java @@ -9,9 +9,18 @@ package ch.ethz.seb.sebserver.webservice.integration.api.exam; import static org.junit.Assert.assertNotNull; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.Test; +import org.springframework.boot.json.JacksonJsonParser; +import org.springframework.http.MediaType; import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; @Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" }) public class ExamAPIAccessTokenRequestTest extends ExamAPIIntegrationTester { @@ -22,4 +31,23 @@ public class ExamAPIAccessTokenRequestTest extends ExamAPIIntegrationTester { assertNotNull(accessToken); } + @Test + public void testAccessTokenResponse() throws Exception { + final MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("grant_type", "client_credentials"); + params.add("scope", "read write"); + + final ResultActions result = this.mockMvc.perform(post("/oauth/token") + .params(params) + .with(httpBasic("test", "test")) + .accept(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)); + + final String resultString = result.andReturn().getResponse().getContentAsString(); + final JacksonJsonParser jsonParser = new JacksonJsonParser(); + final Object expiry = jsonParser.parseMap(resultString).get("expires_in"); + assertNotNull(expiry); + } + } diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/SebConnectionTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/SebConnectionTest.java index 17eda66e..883a9c4b 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/SebConnectionTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/SebConnectionTest.java @@ -13,6 +13,7 @@ import static org.junit.Assert.*; import java.util.Collection; import java.util.List; +import org.junit.Before; import org.junit.Test; import org.mybatis.dynamic.sql.SqlBuilder; import org.springframework.beans.factory.annotation.Autowired; @@ -30,11 +31,15 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType; import ch.ethz.seb.sebserver.gbl.model.session.IndicatorValue; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientConnectionRecord; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ClientConnectionDataInternal; import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ExamSessionCacheService; @@ -47,6 +52,16 @@ public class SebConnectionTest extends ExamAPIIntegrationTester { private ClientEventRecordMapper clientEventRecordMapper; @Autowired private JSONMapper jsonMapper; + @Autowired + private ExamDAO examDAO; + @Autowired + private LmsAPIService lmsAPIService; + + @Before + public void init() { + final LmsAPITemplate lmsAPITemplate = this.lmsAPIService.getLmsAPITemplate(1L).getOrThrow(); + this.examDAO.updateQuizData(2L, lmsAPITemplate.getQuiz("quiz6").getOrThrow(), "testUpdate"); + } @Test @Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" }) @@ -54,7 +69,7 @@ public class SebConnectionTest extends ExamAPIIntegrationTester { final String accessToken = super.obtainAccessToken("test", "test", "SEBClient"); assertNotNull(accessToken); - final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, null); + final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, 2L); assertNotNull(createConnection); // check correct response @@ -71,13 +86,14 @@ public class SebConnectionTest extends ExamAPIIntegrationTester { // check correct stored final List records = this.clientConnectionRecordMapper .selectByExample() + .where(ClientConnectionRecordDynamicSqlSupport.examId, SqlBuilder.isEqualTo(2L)) .build() .execute(); assertTrue(records.size() == 1); final ClientConnectionRecord clientConnectionRecord = records.get(0); assertEquals("1", String.valueOf(clientConnectionRecord.getInstitutionId())); - assertNull(clientConnectionRecord.getExamId()); + assertEquals("2", clientConnectionRecord.getExamId().toString()); assertEquals("CONNECTION_REQUESTED", String.valueOf(clientConnectionRecord.getStatus())); assertEquals(connectionToken, clientConnectionRecord.getConnectionToken()); assertNotNull(clientConnectionRecord.getClientAddress()); @@ -100,6 +116,7 @@ public class SebConnectionTest extends ExamAPIIntegrationTester { @Test @Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" }) public void testCreateConnectionWithExamId() throws Exception { + final String accessToken = super.obtainAccessToken("test", "test", "SEBClient"); assertNotNull(accessToken); @@ -120,6 +137,7 @@ public class SebConnectionTest extends ExamAPIIntegrationTester { // check correct stored final List records = this.clientConnectionRecordMapper .selectByExample() + .where(ClientConnectionRecordDynamicSqlSupport.examId, SqlBuilder.isEqualTo(2L)) .build() .execute(); @@ -152,7 +170,7 @@ public class SebConnectionTest extends ExamAPIIntegrationTester { new TypeReference>() { }); final APIMessage error = errorMessage.iterator().next(); - assertEquals(ErrorMessage.UNEXPECTED.messageCode, error.messageCode); + assertEquals(ErrorMessage.ILLEGAL_API_ARGUMENT.messageCode, error.messageCode); assertEquals("The exam 1 is not running", error.details); } @@ -209,6 +227,7 @@ public class SebConnectionTest extends ExamAPIIntegrationTester { // check correct stored final List records = this.clientConnectionRecordMapper .selectByExample() + .where(ClientConnectionRecordDynamicSqlSupport.examId, SqlBuilder.isEqualTo(2L)) .build() .execute(); @@ -289,6 +308,7 @@ public class SebConnectionTest extends ExamAPIIntegrationTester { // check correct stored final List records = this.clientConnectionRecordMapper .selectByExample() + .where(ClientConnectionRecordDynamicSqlSupport.examId, SqlBuilder.isEqualTo(2L)) .build() .execute(); @@ -351,6 +371,7 @@ public class SebConnectionTest extends ExamAPIIntegrationTester { // check correct stored (no changes) final List records = this.clientConnectionRecordMapper .selectByExample() + .where(ClientConnectionRecordDynamicSqlSupport.examId, SqlBuilder.isNull()) .build() .execute(); @@ -413,6 +434,7 @@ public class SebConnectionTest extends ExamAPIIntegrationTester { // check correct stored (no changes) final List records = this.clientConnectionRecordMapper .selectByExample() + .where(ClientConnectionRecordDynamicSqlSupport.examId, SqlBuilder.isEqualTo(2L)) .build() .execute(); diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/SebVdiConnectionTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/SebVdiConnectionTest.java index a63c9b91..5e722f06 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/SebVdiConnectionTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/exam/SebVdiConnectionTest.java @@ -13,6 +13,7 @@ import static org.junit.Assert.*; import java.util.List; import org.junit.Test; +import org.mybatis.dynamic.sql.SqlBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.Cache.ValueWrapper; @@ -22,6 +23,7 @@ import org.springframework.test.context.jdbc.Sql; import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientConnectionRecord; @@ -43,7 +45,7 @@ public class SebVdiConnectionTest extends ExamAPIIntegrationTester { final String accessToken = super.obtainAccessToken("testVDI", "testVDI", "read write"); assertNotNull(accessToken); - final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, null); + final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, 2L); assertNotNull(createConnection); // check correct response @@ -60,13 +62,14 @@ public class SebVdiConnectionTest extends ExamAPIIntegrationTester { // check correct stored final List records = this.clientConnectionRecordMapper .selectByExample() + .where(ClientConnectionRecordDynamicSqlSupport.examId, SqlBuilder.isEqualTo(2L)) .build() .execute(); assertTrue(records.size() == 1); final ClientConnectionRecord clientConnectionRecord = records.get(0); assertEquals("1", String.valueOf(clientConnectionRecord.getInstitutionId())); - assertNull(clientConnectionRecord.getExamId()); + assertEquals("2", clientConnectionRecord.getExamId().toString()); assertEquals("CONNECTION_REQUESTED", String.valueOf(clientConnectionRecord.getStatus())); assertEquals(connectionToken, clientConnectionRecord.getConnectionToken()); assertNotNull(clientConnectionRecord.getClientAddress()); diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/SEBClientEventCSVExporterTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/SEBClientEventCSVExporterTest.java index 3b7462b5..131400be 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/SEBClientEventCSVExporterTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/SEBClientEventCSVExporterTest.java @@ -12,12 +12,17 @@ import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.joda.time.DateTime; import org.junit.Assert; import org.junit.Test; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; +import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientConnectionRecord; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord; @@ -107,11 +112,16 @@ public class SEBClientEventCSVExporterTest { @Test public void streamDataTestWithExam() { + + final Map attrs = Stream.of(new Tuple<>(QuizData.QUIZ_ATTR_DESCRIPTION, "description")) + .collect(Collectors.toMap(t -> t._1, t -> t._2)); + final SEBClientEventCSVExporter exporter = new SEBClientEventCSVExporter(); final ClientEventRecord event = new ClientEventRecord(0L, 1L, 2, 3L, 4L, new BigDecimal(5), "text"); - final Exam exam = new Exam(0L, 1L, 3L, "externalid", "name", "description", new DateTime(1L), new DateTime(1L), - "startURL", Exam.ExamType.BYOD, "owner", new ArrayList<>(), Exam.ExamStatus.RUNNING, false, "bek", true, - "lastUpdate", 4L, null, null); + final Exam exam = new Exam(0L, 1L, 3L, "externalid", true, "name", new DateTime(1L), + new DateTime(1L), + Exam.ExamType.BYOD, "owner", new ArrayList<>(), Exam.ExamStatus.RUNNING, false, "bek", true, + "lastUpdate", 4L, null, attrs); final ByteArrayOutputStream stream = new ByteArrayOutputStream(); final BufferedOutputStream output = new BufferedOutputStream(stream); @@ -127,14 +137,19 @@ public class SEBClientEventCSVExporterTest { @Test public void streamDataTestWithConnectionAndExam() { + + final Map attrs = Stream.of(new Tuple<>(QuizData.QUIZ_ATTR_DESCRIPTION, "description")) + .collect(Collectors.toMap(t -> t._1, t -> t._2)); + final ClientConnectionRecord connection = new ClientConnectionRecord(0L, 1L, 2L, "status", "token", "sessionid", "clientaddress", "virtualaddress", 3, "vdi", 4L, 5L, 6L, 7, "seb_os_name", "seb_machine_name", "seb_version"); final SEBClientEventCSVExporter exporter = new SEBClientEventCSVExporter(); final ClientEventRecord event = new ClientEventRecord(0L, 1L, 2, 3L, 4L, new BigDecimal(5), "text"); - final Exam exam = new Exam(0L, 1L, 3L, "externalid", "name", "description", new DateTime(1L), new DateTime(1L), - "startURL", Exam.ExamType.BYOD, "owner", new ArrayList<>(), Exam.ExamStatus.RUNNING, false, "bek", true, - "lastUpdate", 4L, null, null); + final Exam exam = new Exam(0L, 1L, 3L, "externalid", true, "name", new DateTime(1L), + new DateTime(1L), + Exam.ExamType.BYOD, "owner", new ArrayList<>(), Exam.ExamStatus.RUNNING, false, "bek", true, + "lastUpdate", 4L, null, attrs); final ByteArrayOutputStream stream = new ByteArrayOutputStream(); final BufferedOutputStream output = new BufferedOutputStream(stream); diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccessTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleCourseAccessTest.java similarity index 93% rename from src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccessTest.java rename to src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleCourseAccessTest.java index 8cd82b35..5154e9b0 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccessTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/legacy/MoodleCourseAccessTest.java @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle; +package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.*; @@ -22,14 +22,12 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; -import ch.ethz.seb.sebserver.gbl.async.AsyncRunner; -import ch.ethz.seb.sebserver.gbl.async.AsyncService; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; 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.ExamineeAccountDetails; import ch.ethz.seb.sebserver.gbl.util.Result; -import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory.MoodleAPIRestTemplate; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleRestTemplateFactory.MoodleAPIRestTemplate; public class MoodleCourseAccessTest { @@ -75,7 +73,6 @@ public class MoodleCourseAccessTest { new JSONMapper(), moodleRestTemplateFactory, null, - new AsyncService(new AsyncRunner()), this.env); final String examId = "123"; @@ -123,10 +120,9 @@ public class MoodleCourseAccessTest { new JSONMapper(), moodleRestTemplateFactory, null, - mock(AsyncService.class), this.env); - final LmsSetupTestResult initAPIAccess = moodleCourseAccess.initAPIAccess(); + final LmsSetupTestResult initAPIAccess = moodleCourseAccess.testCourseAccessAPI(); assertNotNull(initAPIAccess); assertFalse(initAPIAccess.errors.isEmpty()); assertTrue(initAPIAccess.hasError(ErrorType.TOKEN_REQUEST)); @@ -145,10 +141,9 @@ public class MoodleCourseAccessTest { new JSONMapper(), moodleRestTemplateFactory, null, - mock(AsyncService.class), this.env); - final LmsSetupTestResult initAPIAccess = moodleCourseAccess.initAPIAccess(); + final LmsSetupTestResult initAPIAccess = moodleCourseAccess.testCourseAccessAPI(); assertNotNull(initAPIAccess); assertFalse(initAPIAccess.errors.isEmpty()); assertTrue(initAPIAccess.hasError(ErrorType.QUIZ_ACCESS_API_REQUEST)); @@ -166,10 +161,9 @@ public class MoodleCourseAccessTest { new JSONMapper(), moodleRestTemplateFactory, null, - mock(AsyncService.class), this.env); - final LmsSetupTestResult initAPIAccess = moodleCourseAccess.initAPIAccess(); + final LmsSetupTestResult initAPIAccess = moodleCourseAccess.testCourseAccessAPI(); assertNotNull(initAPIAccess); assertTrue(initAPIAccess.errors.isEmpty()); diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamJITSIProctoringServiceTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamJITSIProctoringServiceTest.java index d5741993..6e1db631 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamJITSIProctoringServiceTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamJITSIProctoringServiceTest.java @@ -81,7 +81,7 @@ public class ExamJITSIProctoringServiceTest { final Cryptor cryptorMock = Mockito.mock(Cryptor.class); Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn(Result.of("fbvgeghergrgrthrehreg123")); final JitsiProctoringService examJITSIProctoringService = - new JitsiProctoringService(null, null, cryptorMock, null, new JSONMapper()); + new JitsiProctoringService(null, null, cryptorMock, null, new JSONMapper(), null); String accessToken = examJITSIProctoringService.createPayload( "test-app", @@ -115,7 +115,7 @@ public class ExamJITSIProctoringServiceTest { final Cryptor cryptorMock = Mockito.mock(Cryptor.class); Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn(Result.of("fbvgeghergrgrthrehreg123")); final JitsiProctoringService examJITSIProctoringService = - new JitsiProctoringService(null, null, cryptorMock, null, new JSONMapper()); + new JitsiProctoringService(null, null, cryptorMock, null, new JSONMapper(), null); final ProctoringRoomConnection data = examJITSIProctoringService.createProctoringConnection( "connectionToken", "https://seb-jitsi.example.ch", @@ -160,7 +160,7 @@ public class ExamJITSIProctoringServiceTest { examSessionService, cryptor, clientHttpRequestFactoryService, - jsonMapper); + jsonMapper, null); } } diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties index 3d9af73e..6c98d938 100644 --- a/src/test/resources/application-test.properties +++ b/src/test/resources/application-test.properties @@ -34,8 +34,6 @@ sebserver.webservice.api.admin.refreshTokenValiditySeconds=-1 sebserver.webservice.api.exam.endpoint=/exam-api sebserver.webservice.api.exam.endpoint.discovery=${sebserver.webservice.api.exam.endpoint}/discovery sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1 -sebserver.webservice.api.exam.accessTokenValiditySeconds=1800 -sebserver.webservice.api.exam.refreshTokenValiditySeconds=-1 sebserver.webservice.api.redirect.unauthorized=none # comma separated list of known possible OpenEdX API access token request endpoints sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token diff --git a/src/test/resources/data-test-additional.sql b/src/test/resources/data-test-additional.sql index c23236b2..377469b1 100644 --- a/src/test/resources/data-test-additional.sql +++ b/src/test/resources/data-test-additional.sql @@ -3,18 +3,20 @@ INSERT IGNORE INTO lms_setup VALUES ; INSERT IGNORE INTO seb_client_configuration VALUES - (1, 1, 'test', '2019-07-02 09:22:50', 'test', '98ac3c953abf5948d9d13c81cab580819ee2624c76d6d4147d4896a5b79f49956d382c08c93cb3b9ae350b32', null, 1), - (2, 1, 'testVDI', '2019-07-02 09:22:50', 'testVDI', '8b92ecd63305c97c359b5f39ba707356e2487cf118eb783b92fb7c6cdb217f87709fefa03abb13f8b4e5a14fa2c2ba', null, 1) + (1, 1, 'test', '2019-07-02 09:22:50', 'test', '98ac3c953abf5948d9d13c81cab580819ee2624c76d6d4147d4896a5b79f49956d382c08c93cb3b9ae350b32', null, 1, null, null), + (2, 1, 'testVDI', '2019-07-02 09:22:50', 'testVDI', '8b92ecd63305c97c359b5f39ba707356e2487cf118eb783b92fb7c6cdb217f87709fefa03abb13f8b4e5a14fa2c2ba', null, 1, null, null) ; INSERT IGNORE INTO additional_attributes VALUES (1, 'SEB_CLIENT_CONFIGURATION', 2, 'vdiSetup', 'VM_WARE'), - (2, 'SEB_CLIENT_CONFIGURATION', 2, 'vdiExecutable', 'vmware-view.exe') + (2, 'SEB_CLIENT_CONFIGURATION', 2, 'vdiExecutable', 'vmware-view.exe'), + (3, 'EXAM', 2, 'quiz_start_url', 'https://test.lms.mockup') ; INSERT IGNORE INTO exam VALUES - (1, 1, 1, 'quiz1', 'super-admin', 'super-admin', 'MANAGED', null, null, 'UP_COMING', 1, 0, null, 1, null, null), - (2, 1, 1, 'quiz6', 'super-admin', 'super-admin', 'MANAGED', null, null, 'RUNNING', 1, 0, null, 1, null, null) + (1, 1, 1, 'quiz1', 'admin', 'admin', 'MANAGED', null, null, 'UP_COMING', 1, 0, null, 1, null, null, 'quiz1', null, null, 1), + (2, 1, 1, 'quiz6', 'admin', 'admin', 'MANAGED', null, null, 'RUNNING', 1, 0, null, 1, null, null, 'quiz6', null, null, 1), + (3, 1, 1, 'quiz3', 'admin', 'admin', 'MANAGED', null, null, 'FINISHED', 1, 0, null, 1, null, null, 'quiz3', null, null, 1) ; INSERT IGNORE INTO indicator VALUES @@ -525,7 +527,7 @@ INSERT IGNORE INTO orientation VALUES ; INSERT IGNORE INTO configuration_node VALUES - (1, 1, 0, 'super-admin', 'test', null, 'EXAM_CONFIG', 'READY_TO_USE') + (1, 1, 0, 'super-admin', 'test', null, 'EXAM_CONFIG', 'READY_TO_USE', null, null) ; INSERT IGNORE INTO configuration VALUES @@ -874,3 +876,10 @@ INSERT IGNORE INTO exam_configuration_map VALUES (1, 1, 2, 1, null, null) ; +INSERT IGNORE INTO client_connection VALUES + (1,1,3,'CLOSED','62c4bbdc-e8a5-42bc-8161-c3d187360184','-- (connection_080)','127.0.0.1',NULL,0,NULL,1647960776988,1647960797388,NULL,NULL,'machineName','osName','versionXY'), + (2,1,3,'CLOSED','e3abf4e5-ee5c-4cf1-a13a-1ff8fd23718a','-- (connection_063)','127.0.0.1',NULL,0,NULL,1647960776989,1647960797482,NULL,NULL,'machineName','osName','versionXY'), + (3,1,3,'DISABLED','3120f91e-76a9-4810-8628-f25f4099c47b','-- (connection_043)','127.0.0.1',NULL,0,NULL,1647960814460,1648130312083,NULL,NULL,'machineName','osName','versionXY'), + (4,1,3,'ACTIVE','6b901e7f-65fb-425f-a303-25e9d6fcbdf2','-- (connection_003)','127.0.0.1',NULL,0,NULL,1648134709401,1648542395328,NULL,NULL,'machineName','osName','versionXY'), + (5,1,3,'ACTIVE','2a2013a6-1664-4798-8f1d-362e9ec0a4e4','-- (connection_038)','127.0.0.1',NULL,0,NULL,1648135999634,1648542400438,NULL,NULL,'machineName','osName','versionXY'); + diff --git a/src/test/resources/schema-test.sql b/src/test/resources/schema-test.sql index b72c79e0..93d5a6ef 100644 --- a/src/test/resources/schema-test.sql +++ b/src/test/resources/schema-test.sql @@ -70,6 +70,10 @@ CREATE TABLE IF NOT EXISTS `exam` ( `active` INT(1) NOT NULL, `exam_template_id` BIGINT UNSIGNED NULL, `last_modified` BIGINT NULL, + `quiz_name` VARCHAR(255) NULL, + `quiz_start_time` DATETIME NULL, + `quiz_end_time` DATETIME NULL, + `lms_available` INT(1) NULL, PRIMARY KEY (`id`), INDEX `lms_setup_key_idx` (`lms_setup_id` ASC), INDEX `institution_key_idx` (`institution_id` ASC), @@ -215,6 +219,8 @@ CREATE TABLE IF NOT EXISTS `configuration_node` ( `description` VARCHAR(4000) NULL, `type` VARCHAR(45) NULL, `status` VARCHAR(45) NOT NULL, + `last_update_time` BIGINT UNSIGNED NULL, + `last_update_user` VARCHAR(255) NULL, PRIMARY KEY (`id`), INDEX `configurationInstitutionRef_idx` (`institution_id` ASC), CONSTRAINT `configurationInstitutionRef` @@ -535,6 +541,8 @@ CREATE TABLE IF NOT EXISTS `seb_client_configuration` ( `client_secret` VARCHAR(4000) NOT NULL, `encrypt_secret` VARCHAR(255) NULL, `active` INT(1) NOT NULL, + `last_update_time` BIGINT UNSIGNED NULL, + `last_update_user` VARCHAR(255) NULL, PRIMARY KEY (`id`), INDEX `sebClientCredentialsInstitutionRef_idx` (`institution_id` ASC), CONSTRAINT `sebClientConfigInstitutionRef` @@ -548,7 +556,6 @@ CREATE TABLE IF NOT EXISTS `seb_client_configuration` ( -- ----------------------------------------------------- -- Table `webservice_server_info` -- ----------------------------------------------------- -DROP TABLE IF EXISTS `webservice_server_info` ; CREATE TABLE IF NOT EXISTS `webservice_server_info` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, @@ -645,8 +652,10 @@ DROP TABLE IF EXISTS `batch_action` ; CREATE TABLE IF NOT EXISTS `batch_action` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `institution_id` BIGINT UNSIGNED NOT NULL, + `owner` VARCHAR(255) NULL, `action_type` VARCHAR(45) NOT NULL, - `source_ids` VARCHAR(8000) NULL, + `attributes` VARCHAR(4000) NULL, + `source_ids` VARCHAR(4000) NULL, `successful` VARCHAR(4000) NULL, `last_update` BIGINT NULL, `processor_id` VARCHAR(45) NULL,