diff --git a/checkstyle.xml b/checkstyle.xml index 56ab44e3c622..ec22ce48e21b 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -20,5 +20,6 @@ + diff --git a/docker/atlassian/atlassian-setup.sh b/docker/atlassian/atlassian-setup.sh index 5d70b745596c..1784bbda2444 100755 --- a/docker/atlassian/atlassian-setup.sh +++ b/docker/atlassian/atlassian-setup.sh @@ -1,52 +1,12 @@ #!/bin/bash # for configuring instance over script -jira_internal_port=8080 jira_external_port=8081 -bamboo_port=8085 -bitbucket_port=7990 - -same_credentials=false set -e - -while true; do - read -p $'Are you using the same Username + Password on all three systems (Jira, Bamboo, Bitbucket)? \n' yn - case $yn in - [Yy]* ) same_credentials=true; break;; - [Nn]* ) same_credentials=false; break;; - * ) echo "Please answer yes or no.";; - esac -done - -if [ "$same_credentials" = true ] ; then - echo In order to work, you need to provide the Username and Password of an admin account for Jira, Bamboo and Bitbucket - echo Admin Account for ALL Systems - read -p 'Username: ' all_uservar - read -sp 'Password: ' all_passvar - echo - jira_uservar=$all_uservar - jira_passvar=$all_passvar - bamboo_uservar=$all_uservar - bamboo_passvar=$all_passvar - bitbucket_uservar=$all_uservar - bitbucket_passvar=$all_passvar -else - echo In order to work, you need to provide the Username and Password of an admin account for Jira, Bamboo and Bitbucket - echo Jira Admin Account - read -p 'Username: ' jira_uservar - read -sp 'Password: ' jira_passvar - echo - - echo Bamboo Admin Account - read -p 'Username: ' bamboo_uservar - read -sp 'Password: ' bamboo_passvar - echo - - echo Bitbucket Admin Account - read -p 'Username: ' bitbucket_uservar - read -sp 'Password: ' bitbucket_passvar - echo -fi +echo In order to work, you need to provide the Username and Password of an admin account for Jira +echo Jira Admin Account +read -p 'Username: ' jira_uservar +read -sp 'Password: ' jira_passvar # create groups @@ -113,190 +73,3 @@ for i in {1..20}; do }" \ "$jira_group_add_url$group" done - -# Application Links - -jira_url="http://localhost:$jira_external_port/rest/applinks/latest/applicationlinkForm/createAppLink" -bamboo_url="http://localhost:$bamboo_port/rest/applinks/latest/applicationlinkForm/createAppLink" -bitbucket_url="http://localhost:$bitbucket_port/rest/applinks/latest/applicationlinkForm/createAppLink" - -internal_jira_url="http://jira:$jira_internal_port" -internal_bamboo_url="http://bamboo:$bamboo_port" -internal_bitbucket_url="http://bitbucket:$bitbucket_port" - -echo $'\nConfiguring ApplicationLinks' -# Jira -# add link from jira to bitbucket -curl -u "$jira_uservar":"$jira_passvar" \ - -s \ - --fail \ - --show-error \ - --header "Content-Type: application/json" \ - --request POST \ - --data "{ - \"applicationLink\": { - \"typeId\": \"stash\", - \"name\": \"LS1 Bitbucket Server\", - \"displayUrl\": \"$internal_bitbucket_url\", - \"rpcUrl\": \"$internal_bitbucket_url\", - \"isPrimary\": true, - \"isSystem\": true - }, - \"username\": \"$bitbucket_uservar\", - \"password\": \"$bitbucket_passvar\", - \"customRpcURL\": false, - \"rpcUrl\": \"$internal_jira_url\", - \"createTwoWayLink\": false, - \"configFormValues\": { - \"trustEachOther\": true, - \"shareUserbase\": true - } - }" \ - $jira_url -# add link from jira to bamboo -curl -u "$jira_uservar":"$jira_passvar" \ - --fail \ - --show-error \ - --header "Content-Type: application/json" \ - --request POST \ - --data "{ - \"applicationLink\": { - \"typeId\": \"bamboo\", - \"name\": \"LS1 Bamboo Server\", - \"displayUrl\": \"$internal_bamboo_url\", - \"rpcUrl\": \"$internal_bamboo_url\", - \"isPrimary\": true, - \"isSystem\": true - }, - \"username\": \"$bamboo_uservar\", - \"password\": \"$bamboo_passvar\", - \"customRpcURL\": false, - \"rpcUrl\": \"$internal_jira_url\", - \"createTwoWayLink\": false, - \"configFormValues\": { - \"trustEachOther\": true, - \"shareUserbase\": true - } - }" \ - $jira_url -# Bamboo -# add link from bamboo to bitbucket -curl -u "$bamboo_uservar":"$bamboo_passvar" \ - --fail \ - --show-error \ - --header "Content-Type: application/json" \ - --request POST \ - --data "{ - \"applicationLink\": { - \"typeId\": \"stash\", - \"name\": \"LS1 Bitbucket Server\", - \"displayUrl\": \"$internal_bitbucket_url\", - \"rpcUrl\": \"$internal_bitbucket_url\", - \"isPrimary\": true, - \"isSystem\": true - }, - \"username\": \"$bitbucket_uservar\", - \"password\": \"$bitbucket_passvar\", - \"customRpcURL\": false, - \"rpcUrl\": \"$internal_bamboo_url\", - \"createTwoWayLink\": false, - \"configFormValues\": { - \"trustEachOther\": true, - \"shareUserbase\": true - } - }" \ - $bamboo_url -# add link from bamboo to jira -curl -u "$bamboo_uservar":"$bamboo_passvar" \ - --fail \ - --show-error \ - --header "Content-Type: application/json" \ - --request POST \ - --data "{ - \"applicationLink\": { - \"typeId\": \"jira\", - \"name\": \"LS1 Jira Server\", - \"displayUrl\": \"$internal_jira_url\", - \"rpcUrl\": \"$internal_jira_url\", - \"isPrimary\": true, - \"isSystem\": true - }, - \"username\": \"$jira_uservar\", - \"password\": \"$jira_passvar\", - \"customRpcURL\": false, - \"rpcUrl\": \"$internal_bamboo_url\", - \"createTwoWayLink\": false, - \"configFormValues\": { - \"trustEachOther\": true, - \"shareUserbase\": true - } - }" \ - $bamboo_url -#Bitbucket -# add link from bitbucket to jira -curl -u "$bitbucket_uservar":"$bitbucket_passvar" \ - --fail \ - --show-error \ - --header "Content-Type: application/json" \ - --request POST \ - --data "{ - \"applicationLink\": { - \"typeId\": \"jira\", - \"name\": \"LS1 Jira Server\", - \"displayUrl\": \"$internal_jira_url\", - \"rpcUrl\": \"$internal_jira_url\", - \"isPrimary\": true, - \"isSystem\": true - }, - \"username\": \"$jira_uservar\", - \"password\": \"$jira_passvar\", - \"customRpcURL\": false, - \"rpcUrl\": \"$internal_bitbucket_url\", - \"createTwoWayLink\": false, - \"configFormValues\": { - \"trustEachOther\": true, - \"shareUserbase\": true - } - }" \ - $bitbucket_url -# add link from bitbucket to bamboo -curl -u "$bitbucket_uservar":"$bitbucket_passvar" \ - --fail \ - --show-error \ - --header "Content-Type: application/json" \ - --request POST \ - --data "{ - \"applicationLink\": { - \"typeId\": \"bamboo\", - \"name\": \"LS1 Bamboo Server\", - \"displayUrl\": \"$internal_bamboo_url\", - \"rpcUrl\": \"$internal_bamboo_url\", - \"isPrimary\": true, - \"isSystem\": true - }, - \"username\": \"$bamboo_uservar\", - \"password\": \"$bamboo_passvar\", - \"customRpcURL\": false, - \"rpcUrl\": \"$internal_bitbucket_url\", - \"createTwoWayLink\": false, - \"configFormValues\": { - \"trustEachOther\": true, - \"shareUserbase\": true - } - }" \ - $bitbucket_url -echo ApplicationLinks created -echo Please finish configuration of ApplicationLinks in browser - -jira_application_links_url="http://localhost:$jira_external_port/plugins/servlet/applinks/listApplicationLinks" -bamboo_application_links_url="http://localhost:$bamboo_port/plugins/servlet/applinks/listApplicationLinks" -bitbucket_application_links_url="http://localhost:$bitbucket_port/plugins/servlet/applinks/listApplicationLinks" - -if [ "$(uname)" == "Darwin" ] -then - open $jira_application_links_url $bamboo_application_links_url $bitbucket_application_links_url -else - xdg-open $jira_application_links_url - xdg-open $bamboo_application_links_url - xdg-open $bitbucket_application_links_url -fi diff --git a/docs/dev/setup/bamboo-bitbucket-jira.rst b/docs/dev/setup/bamboo-bitbucket-jira.rst index 11e7bbb7605d..d23bf5e4d8c4 100644 --- a/docs/dev/setup/bamboo-bitbucket-jira.rst +++ b/docs/dev/setup/bamboo-bitbucket-jira.rst @@ -90,78 +90,10 @@ under ``localhost:7990``. #. Make sure that Jira, Bitbucket and Bamboo have finished starting up. - (Only Linux & Windows) Make sure that `xdg-utils `__ - is installed before running the following script. - - .. raw:: html - -
- xdg-utils for Windows users - An easy way to use the xdg-utils on Windows would be to install them on the linux-subsystem, - which should be activated anyways when running Docker on Windows. - For the installation on the subsystem the above linked explanation can be used. -
- Make sure to execute the script from the subsystem. -
- - Execute the shell script ``atlassian-setup.sh`` in the ``docker/atlassian`` directory (e.g. with ``./docker/atlassian/atlassian-setup.sh``). This script creates groups, users and assigns the user to their respective group. - In addition, it configures disabled application links between the 3 applications. - - -#. Enable the created `application - links `__ - between all 3 application (OAuth Impersonate). The links should open automatically after the shell script - has finished. If not open them manually: - - - Bitbucket: http://localhost:7990/plugins/servlet/applinks/listApplicationLinks - - Bamboo: http://localhost:8085/plugins/servlet/applinks/listApplicationLinks - - Jira: http://localhost:8081/plugins/servlet/applinks/listApplicationLinks - - **You manually have to adjust the Display URL for the Bamboo → Bitbucket AND - Bitbucket → Bamboo URl to** ``http://localhost:7990`` **and** - ``http://localhost:8085`` **.** - - .. list-table:: - :widths: 33 33 33 - :header-rows: 1 - - * - **Bamboo:** - - **Bitbucket:** - - **Jira:** - * - .. figure:: setup/bamboo-bitbucket-jira/bamboo_bitbucket_applicationLink.png - :align: center - :target: ../_images/bamboo_bitbucket_applicationLink.png - - Bamboo → Bitbucket - - .. figure:: setup/bamboo-bitbucket-jira/bitbucket_bamboo_applicationLink.png - :align: center - :target: ../_images/bitbucket_bamboo_applicationLink.png - - Bitbucket → Bamboo - - .. figure:: setup/bamboo-bitbucket-jira/jira_bamboo_applicationLink.png - :align: center - :target: ../_images/jira_bamboo_applicationLink.png - - Jira → Bamboo - * - .. figure:: setup/bamboo-bitbucket-jira/bamboo_jira_applicationLink.png - :align: center - :target: ../_images/bamboo_jira_applicationLink.png - - Bamboo → Jira - - .. figure:: setup/bamboo-bitbucket-jira/bitbucket_jira_applicationLink.png - :align: center - :target: ../_images/bitbucket_jira_applicationLink.png - - Bitbucket → Jira - - .. figure:: setup/bamboo-bitbucket-jira/jira_bitbucket_applicationLink.png - :align: center - :target: ../_images/jira_bitbucket_applicationLink.png - - Jira → Bitbucket #. The script *(step 3)* has already created the required users and assigned them to their respective group in Jira. Now, make sure that they are assigned correctly according to the following test setup: diff --git a/docs/dev/setup/bamboo-bitbucket-jira/bamboo_bitbucket_applicationLink.png b/docs/dev/setup/bamboo-bitbucket-jira/bamboo_bitbucket_applicationLink.png deleted file mode 100644 index 34811e929415..000000000000 Binary files a/docs/dev/setup/bamboo-bitbucket-jira/bamboo_bitbucket_applicationLink.png and /dev/null differ diff --git a/docs/dev/setup/bamboo-bitbucket-jira/bamboo_jira_applicationLink.png b/docs/dev/setup/bamboo-bitbucket-jira/bamboo_jira_applicationLink.png deleted file mode 100644 index 97ec96a9a0c8..000000000000 Binary files a/docs/dev/setup/bamboo-bitbucket-jira/bamboo_jira_applicationLink.png and /dev/null differ diff --git a/docs/dev/setup/bamboo-bitbucket-jira/bitbucket_bamboo_applicationLink.png b/docs/dev/setup/bamboo-bitbucket-jira/bitbucket_bamboo_applicationLink.png deleted file mode 100644 index 70222278103a..000000000000 Binary files a/docs/dev/setup/bamboo-bitbucket-jira/bitbucket_bamboo_applicationLink.png and /dev/null differ diff --git a/docs/dev/setup/bamboo-bitbucket-jira/bitbucket_jira_applicationLink.png b/docs/dev/setup/bamboo-bitbucket-jira/bitbucket_jira_applicationLink.png deleted file mode 100644 index f9377e52aeda..000000000000 Binary files a/docs/dev/setup/bamboo-bitbucket-jira/bitbucket_jira_applicationLink.png and /dev/null differ diff --git a/docs/dev/setup/bamboo-bitbucket-jira/jira_bamboo_applicationLink.png b/docs/dev/setup/bamboo-bitbucket-jira/jira_bamboo_applicationLink.png deleted file mode 100644 index b86ed8eb483a..000000000000 Binary files a/docs/dev/setup/bamboo-bitbucket-jira/jira_bamboo_applicationLink.png and /dev/null differ diff --git a/docs/dev/setup/bamboo-bitbucket-jira/jira_bitbucket_applicationLink.png b/docs/dev/setup/bamboo-bitbucket-jira/jira_bitbucket_applicationLink.png deleted file mode 100644 index 570a7a946b6b..000000000000 Binary files a/docs/dev/setup/bamboo-bitbucket-jira/jira_bitbucket_applicationLink.png and /dev/null differ diff --git a/docs/index.rst b/docs/index.rst index 912c3aa9570c..8e622245ddec 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -40,6 +40,7 @@ All these exercises are supposed to be run either live in the lecture with insta user/markdown-support user/exports user/mobile-applications + user/lti .. toctree:: diff --git a/docs/user/lti.rst b/docs/user/lti.rst new file mode 100644 index 000000000000..4f160e2174f9 --- /dev/null +++ b/docs/user/lti.rst @@ -0,0 +1,232 @@ +.. _lti: + +LTI Integration +===================================== + +.. contents:: Content of this document + :local: + :depth: 2 + +Overview +-------- + +LTI (Learning Tools Interoperability) is a standard developed by `IMS Global `_ that allows different learning platforms and tools to work together seamlessly. +It enables tools to integrate smoothly with a learning management system (like Moodle or edX). Artemis supports `LTI 1.1 (deprecated) `_ and `LTI 1.3. `_ +The table below showcases the types of exercises supported by Artemis and their respective functionalities. +For each exercise, students can start the exercise, check their results and get feedback on their work. Assessment results provide a quantitative measure of a student's performance on the exercise, which is represented as a grade or score. Assessment Feedback offers qualitative insights through comments and suggestions. + +.. list-table:: Supported Exercise Interactions + :widths: 25 15 15 15 + :header-rows: 1 + + * - Exercise Type + - Start Exercise + - View Assessment Result + - View Assessment Feedback + * - Programming exercise + - ✔ + - ✔ + - ✔ + * - Quiz exercise + - ✔ + - ✔ + - ✔ + * - Modeling exercise + - ✔ + - ✔ + - ✔ + * - Text exercise + - ✔ + - ✔ + - ✔ + * - File Upload exercise + - ✔ + - ✔ + - ✔ + +Prerequisite for Server Admins +------------------------------- +Before instructors integrate Artemis or students access and start Artemis exercises through Moodle or any other LMS via LTI, server administrators must ensure that LTI is active with the necessary profile on the server. If you are a server admin, please make sure to configure this setting to allow instructors and students to utilize the LTI integration. + +LTI 1.3 Instructor Guide +--------------------------- +Instructors have the capability to seamlessly integrate the Artemis platform with other Learning Management Systems (LMS) like Moodle using the LTI 1.3 standard. The process is straightforward and involves a few key steps: + +#. Connecting Artemis to Other Platforms: Begin by enabling the Online Course setting in Artemis, which activates the LTI features. +#. Retrieve Dynamic Registration URL: Once the Online Course setting is enabled, you can obtain the Dynamic Registration URL from Artemis, which is essential for the integration process. +#. Establish Configuration in Moodle: With the Dynamic Registration URL in hand, proceed to your Moodle platform and input this URL to establish a connection between Artemis and Moodle. +#. Link Specific Exercises: Instructors can also link individual exercises from Artemis directly to their Moodle courses, ensuring students have direct access to specific assignments. + +By following these steps, instructors can effortlessly bridge the gap between Artemis and Moodle, providing students with a unified learning experience. + +1. Connecting Artemis to Other Platforms (e.g., Moodle) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Instructors can connect Artemis online courses via LTI to other Learning Management Systems (e.g., Moodle, edX). +Below given steps for how to configure Artemis over LTI with Moodle. + +1.1 Enable the Online Course Setting in Artemis: +""""""""""""""""""""""""""""""""""""""""""""""""" +Before diving into the LTI configuration, it is crucial to ensure that the Online Course setting is enabled in Artemis. This setting activates the LTI configurability, allowing instructors to link Artemis with Moodle or other LMS platforms. +To enable the Online Course setting in Artemis, follow the steps below:\ + +#. Access Course Management: Start by logging into your Artemis account. Locate and click on the |course-management| option. +#. Select the Desired Course: From the list of available courses, navigate to the course you wish to configure for LTI integration. +#. Edit Course Settings: On the course overview page, you will find an |course_edit| button, located at the top right corner. Clicking this will allow you to modify various course settings. +#. Locate the Online Course Checkbox: As you scroll through the course settings, you will come across a checkbox labeled Online Course. This particular setting is essential for enabling LTI configurability. +#. Activate LTI Configuration: To finalize the process, simply check the Online Course checkbox. By doing so, you are activating the LTI configuration settings for that specific course. Make sure to save any changes made. + +.. figure:: lti/enable_onlinecourse.png + :align: center + :width: 700 + :alt: Enable Online Course + +With the Online Course setting enabled, you can now proceed to integrate Artemis with Moodle using the LTI 1.3 standard. The subsequent sections of this guide will provide detailed steps on achieving this integration. + +1.2. Copy Dynamic Registration URL from Artemis +""""""""""""""""""""""""""""""""""""""""""""""" +Once the above setting is enabled, you can now set up a bridge between Artemis and Moodle. +To retrieve Dynamic Registration URL from Artemis, follow the steps outlined below: + +#. Access Course Management: Begin by logging into your Artemis account. Click on the |course-management| option. +#. Choose the Relevant Course: From the list of courses, select the one you wish to configure for LTI integration. This will lead you to the course's settings and details. +#. Navigate to Course Details: Once inside the course settings, scroll down until you find the Course Details section. +#. Access LTI Configuration: Within the Course Details section, you will find an option labeled LTI Configuration. Click on it to access the LTI settings for the course. + + .. figure:: lti/lticonfiguration_link.png + :align: center + :width: 700 + :alt: Locate LTI Configuration + +#. Switch to LTI 1.3 Tab: Inside the LTI Configuration, there will be multiple tabs related to different LTI versions. Click on the LTI 1.3 tab to access the settings specific to this version. +#. Retrieve Dynamic Registration URL: In the LTI 1.3 settings, locate the Dynamic Registration URL. This URL is essential for integrating Artemis with Moodle. Copy this URL for use in the subsequent Moodle configuration steps. + +.. figure:: lti/lticonfiguration_tab.png + :align: center + :width: 700 + :alt: LTI 1.3 Configuration + + + +1.3. Establish Artemis configuration from Moodle +"""""""""""""""""""""""""""""""""""""""""""""""" + +With the Dynamic Registration URL copied, you can now configure the LTI 1.3 integration in Moodle: + +#. Access Site Administration: Log into your Moodle account. From the main dashboard, navigate to the Site Administration section. This section contains various administrative settings for the Moodle platform. +#. Navigate to External Tool Settings: Inside the Site Administration, go to Plugins. From there, select External tool followed by Manage Tools. This will lead you to the LTI configurations in Moodle. + + .. figure:: lti/moodle_site_administration.png + :align: center + :width: 700 + :alt: Moodle - Site Administration + +#. Enter Dynamic Registration URL: In the Manage Tools section, you will find a field labeled Tool URL. Paste the previously copied Dynamic Registration URL from Artemis into this field. +#. Initiate LTI Advantage Integration: After entering the URL, click on the Add LTI Advantage button. This action will begin the process of integrating Artemis with Moodle using the LTI 1.3 standard. + + .. figure:: lti/moodle_add_tool_url.png + :align: center + :width: 700 + :alt: Moodle - Site Administration + +#. Locate the Artemis Course: Once the integration process starts, scroll down the list until you find the Artemis course identified by its shortname. +#. Activate the Integration: To finalize the integration, click on the Activate button next to the Artemis course name. This action will complete the LTI 1.3 integration between Artemis and Moodle. + +.. figure:: lti/moodle_activate_lti.png + :align: center + :alt: Moodle - Site Administration + +2. Linking Specific Exercises from Artemis to Moodle +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Once the LTI configuration between Artemis and Moodle is successfully completed, instructors have the capability to link specific Artemis exercises directly to their Moodle courses. This integration provides students with direct access to Artemis exercises from their Moodle courses. The process involves two primary steps: +#. Retrieving the LTI 1.3 Launch URL for the desired Artemis exercise. +#. Linking this URL to the Moodle course. +By following the detailed steps below, instructors can integrate individual Artemis exercises into their Moodle courses, enhancing the learning experience for students. + +2.1. Retrieve LTI 1.3 Launch URL from Artemis +""""""""""""""""""""""""""""""""""""""""""""" + +#. Access Course Management: Select the |course-management| to view all your courses. +#. Choose the Desired Course: From the list of available courses, select the one online course that containing the exercise you would like to link to Moodle. +#. Navigate to Course Details: Once you are inside the course settings, scroll down to the Course Details section. +#. Access LTI Configuration: Within the Course Details section, you will find an link labeled LTI Configuration. Click on this to access the LTI settings specific to the course. +#. Switch to the Exercises Tab: Inside the LTI Configuration, locate and click on the Exercises tab. This tab lists all the exercises available for the course. +#. Retrieve the LTI 1.3 Launch URL: For each exercise listed, there is an associated LTI 1.3 Launch URL. This URL is crucial for linking the exercise to Moodle. Find the exercise you wish to link and copy its LTI 1.3 Launch URL. + +.. figure:: lti/ltiexercises_list.png + :align: center + :width: 700 + :alt: LTI Exercise List + +2.2. Link exercise to Moodle +"""""""""""""""""""""""""""" + +With the LTI 1.3 Launch URL copied, you can now link the exercise in Moodle: + +#. Access the Desired Course: Log into your Moodle account and navigate to the course where you want to link the Artemis exercise. +#. Enable Edit Mode: Once inside the course, turn on the Edit Mode. This mode allows you to make changes and add resources to the course. + + .. figure:: lti/moodle_editmode.png + :align: center + :width: 700 + :alt: Moodle - Edit Course + +#. Add an External Tool: Click on the Add an activity or resource button, which brings up a list of available activities and resources. From this list, select the External tool option. + + .. figure:: lti/moodle_add_external_tool.png + :align: center + :width: 700 + :alt: Moodle - Add External Tool + +#. Enter the LTI 1.3 Launch URL: In the settings for the external tool, you will find a field labeled Resource URL. Paste the previously copied LTI 1.3 Launch URL from Artemis into this field. +#. Save and Finalize: After entering the URL, click on the Save and return to course button to finalize the addition. The linked Artemis exercise should now be accessible directly from the Moodle course. + +.. figure:: lti/moodle_add_external_tool_page.png + :align: center + :width: 700 + :alt: Moodle - Add External Tool Page + + +LTI 1.3 Student Guide +--------------------------- +For students, the integration of Artemis with Moodle via LTI 1.3 offers a streamlined experience to access and participate in Artemis exercises directly from the Moodle platform. The key steps involved are: + +#. Starting an Artemis Exercise: Students can easily access their Moodle courses and find the linked Artemis exercises. Clicking on these links will open the Artemis exercise page right within Moodle, providing a smooth transition. +#. First-Time Users: If you are accessing an Artemis exercise for the first time, you will receive a unique password. It is essential to note this password down as it will be required for future Artemis sessions. +#. Viewing Grades and Feedback: Beyond just participating, students can also view their grades and feedback for any evaluated Artemis exercises, all within the Moodle environment. This ensures that students have a one-stop platform to engage with exercises and track their performance. + +By following this guide, students can make the most of the integrated learning experience offered by Artemis and Moodle. + +1. Start Artemis Exercise through Moodle +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Here is a step-by-step guide on how students can start an Artemis exercise through Moodle: + +How to Start an Exercise +"""""""""""""""""""""""" + +#. Access the Moodle Course: Log into your Moodle account and navigate to the specific course containing the linked Artemis exercise. +#. Select the Artemis Exercise: Within the course content, locate and click on the external Artemis exercise you wish to participate in. +#. Launch Artemis from Moodle: Upon selecting the exercise, the Artemis exercise page will open in a frame within Moodle, ensuring a seamless transition between the two platforms. +#. Participate Artemis Exercise: Students can now participate in the Artemis exercise, submit their responses, and receive real-time feedback. + +.. figure:: lti/moodle_artemis_progex.png + :align: center + :width: 700 + :alt: Moodle - Artemis Programming Exercise + +2. First Time Users +^^^^^^^^^^^^^^^^^^^ +If a student is participating in an Artemis exercise for the first time, a pop-up will appear. The pop-up will display a uniquely generated password for the student. This password will be required for future sign-ins to Artemis. It is crucial to copy this password and store it in a safe and accessible location. + +.. figure:: lti/moodle_password_popup.png + :align: center + :width: 700 + :alt: Moodle - Password Pop-up + +3. Viewing Grades and Feedback +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In addition to participating in exercises, students can also view their grades and feedback for evaluated Artemis exercises directly within Moodle. +This integration ensures that students have a centralized location to track their performance and receive constructive feedback. + +.. |course-management| image:: exercises/general/course-management.png +.. |course_edit| image:: courses/customizable/buttons/course_edit.png diff --git a/docs/user/lti/enable_onlinecourse.png b/docs/user/lti/enable_onlinecourse.png new file mode 100644 index 000000000000..ee47fa2cadff Binary files /dev/null and b/docs/user/lti/enable_onlinecourse.png differ diff --git a/docs/user/lti/lticonfiguration_link.png b/docs/user/lti/lticonfiguration_link.png new file mode 100644 index 000000000000..7dfdba10c49c Binary files /dev/null and b/docs/user/lti/lticonfiguration_link.png differ diff --git a/docs/user/lti/lticonfiguration_tab.png b/docs/user/lti/lticonfiguration_tab.png new file mode 100644 index 000000000000..f4d94a5924a4 Binary files /dev/null and b/docs/user/lti/lticonfiguration_tab.png differ diff --git a/docs/user/lti/ltiexercises_list.png b/docs/user/lti/ltiexercises_list.png new file mode 100644 index 000000000000..3e0a0ea8c3fb Binary files /dev/null and b/docs/user/lti/ltiexercises_list.png differ diff --git a/docs/user/lti/moodle_activate_lti.png b/docs/user/lti/moodle_activate_lti.png new file mode 100644 index 000000000000..3fa468273950 Binary files /dev/null and b/docs/user/lti/moodle_activate_lti.png differ diff --git a/docs/user/lti/moodle_add_external_tool.png b/docs/user/lti/moodle_add_external_tool.png new file mode 100644 index 000000000000..c74eb7b823d5 Binary files /dev/null and b/docs/user/lti/moodle_add_external_tool.png differ diff --git a/docs/user/lti/moodle_add_external_tool_page.png b/docs/user/lti/moodle_add_external_tool_page.png new file mode 100644 index 000000000000..7431c3b2432a Binary files /dev/null and b/docs/user/lti/moodle_add_external_tool_page.png differ diff --git a/docs/user/lti/moodle_add_tool_url.png b/docs/user/lti/moodle_add_tool_url.png new file mode 100644 index 000000000000..1c4c53416613 Binary files /dev/null and b/docs/user/lti/moodle_add_tool_url.png differ diff --git a/docs/user/lti/moodle_artemis_progex.png b/docs/user/lti/moodle_artemis_progex.png new file mode 100644 index 000000000000..bc05914bb083 Binary files /dev/null and b/docs/user/lti/moodle_artemis_progex.png differ diff --git a/docs/user/lti/moodle_editmode.png b/docs/user/lti/moodle_editmode.png new file mode 100644 index 000000000000..65f3a13bd057 Binary files /dev/null and b/docs/user/lti/moodle_editmode.png differ diff --git a/docs/user/lti/moodle_password_popup.png b/docs/user/lti/moodle_password_popup.png new file mode 100644 index 000000000000..a602dd96bde1 Binary files /dev/null and b/docs/user/lti/moodle_password_popup.png differ diff --git a/docs/user/lti/moodle_site_administration.png b/docs/user/lti/moodle_site_administration.png new file mode 100644 index 000000000000..ec63f78970d6 Binary files /dev/null and b/docs/user/lti/moodle_site_administration.png differ diff --git a/src/main/java/de/tum/in/www1/artemis/service/FileService.java b/src/main/java/de/tum/in/www1/artemis/service/FileService.java index 814b47092fd6..4de0fa0b9d23 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/FileService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/FileService.java @@ -181,7 +181,7 @@ public URI handleSaveFile(MultipartFile file, boolean keepFilename, boolean mark filePath = generateFilePath(filenamePrefix, fileExtension, path); } try { - FileUtils.copyToFile(file.getInputStream(), filePath.toFile()); + FileUtils.copyInputStreamToFile(file.getInputStream(), filePath.toFile()); return generateResponsePath(filePath, markdown); } @@ -344,7 +344,7 @@ public void copyResource(final Resource resource, final Path prefix, final Path return; } - FileUtils.copyToFile(resource.getInputStream(), targetPath.toFile()); + FileUtils.copyInputStreamToFile(resource.getInputStream(), targetPath.toFile()); if (targetPath.endsWith("gradlew")) { targetPath.toFile().setExecutable(true); diff --git a/src/main/java/de/tum/in/www1/artemis/service/FileUploadSubmissionService.java b/src/main/java/de/tum/in/www1/artemis/service/FileUploadSubmissionService.java index df4454fddcc9..3849af1419d6 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/FileUploadSubmissionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/FileUploadSubmissionService.java @@ -201,7 +201,7 @@ private Path saveFileForSubmission(final MultipartFile file, final Submission su final Path filePath = dirPath.resolve(filename); final File savedFile = filePath.toFile(); - FileUtils.copyToFile(file.getInputStream(), savedFile); + FileUtils.copyInputStreamToFile(file.getInputStream(), savedFile); return filePath; } diff --git a/src/main/java/de/tum/in/www1/artemis/service/RepositoryService.java b/src/main/java/de/tum/in/www1/artemis/service/RepositoryService.java index 8b294f0bdddb..ef39326d10da 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/RepositoryService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/RepositoryService.java @@ -188,7 +188,6 @@ public void createFile(Repository repository, String filePath, InputStream input File file = checkIfPathAndFileAreValidAndReturnSafeFile(repository, safePath); FileUtils.copyToFile(inputStream, file); repository.setContent(null); // invalidate cache - inputStream.close(); } /** @@ -207,7 +206,6 @@ public void createFolder(Repository repository, String folderPath, InputStream i File keep = new File(repository.getLocalPath().resolve(safePath).resolve(".keep"), repository); FileUtils.copyToFile(inputStream, keep); repository.setContent(null); // invalidate cache - inputStream.close(); } /** diff --git a/src/main/java/de/tum/in/www1/artemis/service/TextBlockService.java b/src/main/java/de/tum/in/www1/artemis/service/TextBlockService.java index b8408a5530c1..19b4dc50d31c 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/TextBlockService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/TextBlockService.java @@ -73,8 +73,9 @@ public Set splitSubmissionIntoBlocks(TextSubmission submission) { final int startIndex = start + offset; final int endIndex = startIndex + lineOrSentenceTrimed.length(); start = start + lineOrSentence.length() + LINE_SEPARATOR_LENGTH; - if (startIndex == endIndex || lineOrSentence.isBlank()) + if (startIndex == endIndex || lineOrSentence.isBlank()) { continue; // Do *not* define a text block for an empty line. + } final TextBlock textBlock = new TextBlock().text(lineOrSentenceTrimed).startIndex(startIndex).endIndex(endIndex).submission(submission).automatic(); textBlock.computeId(); diff --git a/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseRepositoryService.java b/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseRepositoryService.java index 65f093083484..2a02704debb3 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseRepositoryService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseRepositoryService.java @@ -546,7 +546,7 @@ private void setupBuildStage(final Path resourcePrefix, final Path templatePath, // staging project files are only required for maven final boolean isMaven = isMavenProject(projectType); if (isMaven && stagePomXml.isPresent()) { - FileUtils.copyFile(stagePomXml.get().getFile(), buildStagePath.resolve(POM_XML).toFile()); + FileUtils.copyInputStreamToFile(stagePomXml.get().getInputStream(), buildStagePath.resolve(POM_XML).toFile()); } final Path buildStageResourcesPath = templatePath.resolve(TEST_FILES_PATH).resolve(buildStageTemplateSubDirectory); diff --git a/src/main/java/de/tum/in/www1/artemis/service/scheduled/cache/quiz/QuizExerciseCache.java b/src/main/java/de/tum/in/www1/artemis/service/scheduled/cache/quiz/QuizExerciseCache.java index 4ae599da59b8..ba37fd740bf2 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/scheduled/cache/quiz/QuizExerciseCache.java +++ b/src/main/java/de/tum/in/www1/artemis/service/scheduled/cache/quiz/QuizExerciseCache.java @@ -78,10 +78,12 @@ public final int hashCode() { @Override public final boolean equals(Object obj) { - if (this == obj) + if (this == obj) { return true; - if (!(obj instanceof QuizExerciseCache)) + } + if (!(obj instanceof QuizExerciseCache)) { return false; + } return Objects.equals(exerciseId, ((QuizExerciseCache) obj).exerciseId); } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/dto/examevent/ExamLiveEventDTO.java b/src/main/java/de/tum/in/www1/artemis/web/rest/dto/examevent/ExamLiveEventDTO.java index cac6c288546b..2426e68f296c 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/dto/examevent/ExamLiveEventDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/dto/examevent/ExamLiveEventDTO.java @@ -52,10 +52,12 @@ public void setCreatedDate(Instant createdDate) { @Override public boolean equals(Object o) { - if (this == o) + if (this == o) { return true; - if (o == null || getClass() != o.getClass()) + } + if (o == null || getClass() != o.getClass()) { return false; + } ExamLiveEventDTO that = (ExamLiveEventDTO) o; return Objects.equals(id, that.id) && Objects.equals(createdBy, that.createdBy) && Objects.equals(createdDate, that.createdDate); } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/dto/examevent/ExamWideAnnouncementEventDTO.java b/src/main/java/de/tum/in/www1/artemis/web/rest/dto/examevent/ExamWideAnnouncementEventDTO.java index 519a01ebbe7a..d223a42e26e4 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/dto/examevent/ExamWideAnnouncementEventDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/dto/examevent/ExamWideAnnouncementEventDTO.java @@ -19,12 +19,15 @@ public void setText(String text) { @Override public boolean equals(Object o) { - if (this == o) + if (this == o) { return true; - if (o == null || getClass() != o.getClass()) + } + if (o == null || getClass() != o.getClass()) { return false; - if (!super.equals(o)) + } + if (!super.equals(o)) { return false; + } ExamWideAnnouncementEventDTO that = (ExamWideAnnouncementEventDTO) o; return Objects.equals(text, that.text); } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/dto/examevent/WorkingTimeUpdateEventDTO.java b/src/main/java/de/tum/in/www1/artemis/web/rest/dto/examevent/WorkingTimeUpdateEventDTO.java index 2abfd0b1032d..4b812ce911df 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/dto/examevent/WorkingTimeUpdateEventDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/dto/examevent/WorkingTimeUpdateEventDTO.java @@ -39,12 +39,15 @@ public void setCourseWide(boolean courseWide) { @Override public boolean equals(Object o) { - if (this == o) + if (this == o) { return true; - if (o == null || getClass() != o.getClass()) + } + if (o == null || getClass() != o.getClass()) { return false; - if (!super.equals(o)) + } + if (!super.equals(o)) { return false; + } WorkingTimeUpdateEventDTO that = (WorkingTimeUpdateEventDTO) o; return newWorkingTime == that.newWorkingTime && oldWorkingTime == that.oldWorkingTime && courseWide == that.courseWide; } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/repository/RepositoryResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/repository/RepositoryResource.java index 5cce9e0333c0..5f44f6581daf 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/repository/RepositoryResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/repository/RepositoryResource.java @@ -170,8 +170,9 @@ public ResponseEntity createFile(Long domainId, String filePath, HttpServl return executeAndCheckForExceptions(() -> { Repository repository = getRepository(domainId, RepositoryActionType.WRITE, true); - InputStream inputStream = request.getInputStream(); - repositoryService.createFile(repository, filePath, inputStream); + try (var inputStream = request.getInputStream()) { + repositoryService.createFile(repository, filePath, inputStream); + } return new ResponseEntity<>(HttpStatus.OK); }); } @@ -189,8 +190,9 @@ public ResponseEntity createFolder(Long domainId, String folderPath, HttpS return executeAndCheckForExceptions(() -> { Repository repository = getRepository(domainId, RepositoryActionType.WRITE, true); - InputStream inputStream = request.getInputStream(); - repositoryService.createFolder(repository, folderPath, inputStream); + try (InputStream inputStream = request.getInputStream()) { + repositoryService.createFolder(repository, folderPath, inputStream); + } return new ResponseEntity<>(HttpStatus.OK); }); } @@ -389,8 +391,8 @@ private void fetchAndUpdateFile(FileSubmission submission, Repository repository throw new IOException("File could not be found."); } - InputStream inputStream = new ByteArrayInputStream(submission.getFileContent().getBytes(StandardCharsets.UTF_8)); - FileUtils.copyToFile(inputStream, file.get()); - inputStream.close(); + try (var inputStream = new ByteArrayInputStream(submission.getFileContent().getBytes(StandardCharsets.UTF_8))) { + FileUtils.copyToFile(inputStream, file.get()); + } } } diff --git a/src/main/resources/templates/java/test/staticCodeAnalysisConfig/checkstyle-configuration.xml b/src/main/resources/templates/java/test/staticCodeAnalysisConfig/checkstyle-configuration.xml index 8f5e762b16f4..b5d640a57efb 100644 --- a/src/main/resources/templates/java/test/staticCodeAnalysisConfig/checkstyle-configuration.xml +++ b/src/main/resources/templates/java/test/staticCodeAnalysisConfig/checkstyle-configuration.xml @@ -23,40 +23,43 @@ - + - + - + - + - + - + + + - + + @@ -65,7 +68,7 @@ - + @@ -74,7 +77,7 @@ - + @@ -87,7 +90,7 @@ - + @@ -99,7 +102,7 @@ - + @@ -107,13 +110,9 @@ - - - - - + @@ -126,12 +125,12 @@ - + - + @@ -139,7 +138,7 @@ - + @@ -156,7 +155,7 @@ - + @@ -164,7 +163,7 @@ - + diff --git a/src/test/java/de/tum/in/www1/artemis/assessment/ExerciseScoresChartIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/assessment/ExerciseScoresChartIntegrationTest.java index b9c1839d4d05..02f2dc151c3d 100644 --- a/src/test/java/de/tum/in/www1/artemis/assessment/ExerciseScoresChartIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/assessment/ExerciseScoresChartIntegrationTest.java @@ -3,8 +3,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +import java.time.Instant; import java.time.ZonedDateTime; import java.util.List; +import java.util.Optional; import java.util.Set; import org.junit.jupiter.api.AfterEach; @@ -13,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.util.ReflectionTestUtils; import de.tum.in.www1.artemis.AbstractSpringIntegrationIndependentTest; import de.tum.in.www1.artemis.course.CourseUtilService; @@ -75,6 +78,9 @@ class ExerciseScoresChartIntegrationTest extends AbstractSpringIntegrationIndepe @BeforeEach void setupTestScenario() { + // Prevents the ParticipantScoreScheduleService from scheduling tasks related to prior results + ReflectionTestUtils.setField(participantScoreScheduleService, "lastScheduledRun", Optional.of(Instant.now())); + ParticipantScoreScheduleService.DEFAULT_WAITING_TIME_FOR_SCHEDULED_TASKS = 50; participantScoreScheduleService.activate(); ZonedDateTime pastTimestamp = ZonedDateTime.now().minusDays(5); diff --git a/src/test/java/de/tum/in/www1/artemis/assessment/ParticipantScoreIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/assessment/ParticipantScoreIntegrationTest.java index 86bb975e28b9..b8591935b428 100644 --- a/src/test/java/de/tum/in/www1/artemis/assessment/ParticipantScoreIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/assessment/ParticipantScoreIntegrationTest.java @@ -3,8 +3,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +import java.time.Instant; import java.time.ZonedDateTime; import java.util.List; +import java.util.Optional; import java.util.Set; import org.junit.jupiter.api.AfterEach; @@ -13,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.util.ReflectionTestUtils; import de.tum.in.www1.artemis.AbstractSpringIntegrationLocalCILocalVCTest; import de.tum.in.www1.artemis.competency.CompetencyUtilService; @@ -97,7 +100,10 @@ class ParticipantScoreIntegrationTest extends AbstractSpringIntegrationLocalCILo @BeforeEach void setupTestScenario() { - ParticipantScoreScheduleService.DEFAULT_WAITING_TIME_FOR_SCHEDULED_TASKS = 50; + // Prevents the ParticipantScoreScheduleService from scheduling tasks related to prior results + ReflectionTestUtils.setField(participantScoreScheduleService, "lastScheduledRun", Optional.of(Instant.now())); + + ParticipantScoreScheduleService.DEFAULT_WAITING_TIME_FOR_SCHEDULED_TASKS = 200; participantScoreScheduleService.activate(); ZonedDateTime pastTimestamp = ZonedDateTime.now().minusDays(5); // creating the users student1, tutor1 and instructors1 diff --git a/src/test/java/de/tum/in/www1/artemis/entitylistener/ResultListenerIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/entitylistener/ResultListenerIntegrationTest.java index fe602b3564b4..cd3eedaaa1ea 100644 --- a/src/test/java/de/tum/in/www1/artemis/entitylistener/ResultListenerIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/entitylistener/ResultListenerIntegrationTest.java @@ -4,9 +4,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -import java.time.Duration; +import java.time.Instant; import java.time.ZonedDateTime; import java.util.List; +import java.util.Optional; import java.util.Set; import org.junit.jupiter.api.AfterEach; @@ -16,6 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.util.ReflectionTestUtils; import de.tum.in.www1.artemis.AbstractSpringIntegrationLocalCILocalVCTest; import de.tum.in.www1.artemis.course.CourseUtilService; @@ -92,8 +94,11 @@ void cleanup() { @BeforeEach void setupTestScenario() { + // Prevents the ParticipantScoreScheduleService from scheduling tasks related to prior results + ReflectionTestUtils.setField(participantScoreScheduleService, "lastScheduledRun", Optional.of(Instant.now())); + + ParticipantScoreScheduleService.DEFAULT_WAITING_TIME_FOR_SCHEDULED_TASKS = 100; participantScoreScheduleService.activate(); - ParticipantScoreScheduleService.DEFAULT_WAITING_TIME_FOR_SCHEDULED_TASKS = 50; ZonedDateTime pastReleaseDate = ZonedDateTime.now().minusDays(5); ZonedDateTime pastDueDate = ZonedDateTime.now().minusDays(3); ZonedDateTime pastAssessmentDueDate = ZonedDateTime.now().minusDays(2); @@ -103,7 +108,6 @@ void setupTestScenario() { idOfStudent1 = student1.getId(); // creating course Course course = courseUtilService.createCourse(); - Long idOfCourse = course.getId(); TextExercise textExercise = textExerciseUtilService.createIndividualTextExercise(course, pastReleaseDate, pastDueDate, pastAssessmentDueDate); idOfIndividualTextExercise = textExercise.getId(); Exercise teamExercise = textExerciseUtilService.createTeamTextExercise(course, pastReleaseDate, pastDueDate, pastAssessmentDueDate); @@ -422,7 +426,7 @@ private ParticipantScore setupTestScenarioWithOneResultSaved(boolean isRatedResu // Wait for the scheduler to execute its task participantScoreScheduleService.executeScheduledTasks(); - await().atMost(Duration.ofSeconds(30)).until(() -> participantScoreScheduleService.isIdle()); + await().until(() -> participantScoreScheduleService.isIdle()); var savedParticipantScores = participantScoreRepository.findAllByExercise(exercise); assertThat(savedParticipantScores).isNotEmpty(); diff --git a/src/test/java/de/tum/in/www1/artemis/exercise/programmingexercise/ProgrammingExerciseTemplateIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/exercise/programmingexercise/ProgrammingExerciseTemplateIntegrationTest.java index ea78546ada41..4d7302e4ce97 100644 --- a/src/test/java/de/tum/in/www1/artemis/exercise/programmingexercise/ProgrammingExerciseTemplateIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/exercise/programmingexercise/ProgrammingExerciseTemplateIntegrationTest.java @@ -84,8 +84,9 @@ static void detectMavenHome() { String m2Home = System.getenv("M2_HOME"); String mavenHome = System.getProperty("maven.home"); - if (m2Home != null || mavenHome != null) + if (m2Home != null || mavenHome != null) { return; + } try { String mvnExecutable = Os.isFamily(Os.FAMILY_WINDOWS) ? "mvn.cmd" : "mvn"; diff --git a/src/test/java/de/tum/in/www1/artemis/iris/AbstractIrisIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/iris/AbstractIrisIntegrationTest.java index f2ccfa6cdfab..2acdb177fc3a 100644 --- a/src/test/java/de/tum/in/www1/artemis/iris/AbstractIrisIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/iris/AbstractIrisIntegrationTest.java @@ -1,7 +1,8 @@ package de.tum.in.www1.artemis.iris; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; import java.util.Objects; import java.util.stream.Collectors; @@ -141,36 +142,4 @@ protected void verifyErrorWasSentOverWebsocket(String user, Long sessionId) { ArgumentMatchers.argThat(object -> object instanceof IrisWebsocketService.IrisWebsocketDTO websocketDTO && websocketDTO.getType() == IrisWebsocketService.IrisWebsocketDTO.IrisWebsocketMessageType.ERROR)); } - - /** - * Verify that nothing was sent through the websocket. - * - * @param user the user - * @param sessionId the session id - */ - protected void verifyNothingWasSentOverWebsocket(String user, Long sessionId) { - verify(websocketMessagingService, times(0)).sendMessageToUser(eq(user), eq("/topic/iris/sessions/" + sessionId), any()); - } - - /** - * Verify that nothing else was sent through the websocket. - * - * @param user the user - * @param sessionId the session id - */ - protected void verifyNothingElseWasSentOverWebsocket(String user, Long sessionId) { - verifyNoMoreInteractions(websocketMessagingService); - } - - /** - * Verify that no error was sent through the websocket. - * - * @param user the user - * @param sessionId the session id - */ - protected void verifyNoErrorWasSentOverWebsocket(String user, Long sessionId) { - verify(websocketMessagingService, times(0)).sendMessageToUser(eq(user), eq("/topic/iris/sessions/" + sessionId), - ArgumentMatchers.argThat(object -> object instanceof IrisWebsocketService.IrisWebsocketDTO websocketDTO - && websocketDTO.getType() == IrisWebsocketService.IrisWebsocketDTO.IrisWebsocketMessageType.ERROR)); - } } diff --git a/src/test/java/de/tum/in/www1/artemis/iris/IrisMessageIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/iris/IrisMessageIntegrationTest.java index f8a538730d9e..b2017f7ebef9 100644 --- a/src/test/java/de/tum/in/www1/artemis/iris/IrisMessageIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/iris/IrisMessageIntegrationTest.java @@ -2,6 +2,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; import java.time.ZonedDateTime; import java.util.List; @@ -90,13 +94,12 @@ void sendOneMessage() throws Exception { verifyMessageWasSentOverWebsocket(TEST_PREFIX + "student1", irisSession.getId(), messageToSend); verifyMessageWasSentOverWebsocket(TEST_PREFIX + "student1", irisSession.getId(), "Hello World"); - verifyNothingElseWasSentOverWebsocket(TEST_PREFIX + "student1", irisSession.getId()); } @Test @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") void sendOneMessageToWrongSession() throws Exception { - var irisSession1 = irisSessionService.createChatSessionForProgrammingExercise(exercise, userUtilService.getUserByLogin(TEST_PREFIX + "student1")); + irisSessionService.createChatSessionForProgrammingExercise(exercise, userUtilService.getUserByLogin(TEST_PREFIX + "student1")); var irisSession2 = irisSessionService.createChatSessionForProgrammingExercise(exercise, userUtilService.getUserByLogin(TEST_PREFIX + "student2")); IrisMessage messageToSend = createDefaultMockMessage(irisSession2); request.postWithResponseBody("/api/iris/sessions/" + irisSession2.getId() + "/messages", messageToSend, IrisMessage.class, HttpStatus.FORBIDDEN); @@ -138,6 +141,8 @@ void sendTwoMessages() throws Exception { .isEqualTo(messageToSend2.getContent().stream().map(IrisMessageContent::getTextContent).toList()); irisSessionFromDb = irisSessionRepository.findByIdWithMessagesElseThrow(irisSession.getId()); assertThat(irisSessionFromDb.getMessages()).hasSize(2).isEqualTo(List.of(irisMessage1, irisMessage2)); + + verify(websocketMessagingService, timeout(3000).times(4)).sendMessageToUser(eq(TEST_PREFIX + "student1"), any(), any()); } @Test @@ -245,7 +250,6 @@ void sendOneMessageBadRequest() throws Exception { verifyMessageWasSentOverWebsocket(TEST_PREFIX + "student1", irisSession.getId(), messageToSend); verifyErrorWasSentOverWebsocket(TEST_PREFIX + "student1", irisSession.getId()); - verifyNothingElseWasSentOverWebsocket(TEST_PREFIX + "student1", irisSession.getId()); } @Test @@ -261,7 +265,6 @@ void sendOneMessageEmptyBody() throws Exception { verifyMessageWasSentOverWebsocket(TEST_PREFIX + "student1", irisSession.getId(), messageToSend); verifyErrorWasSentOverWebsocket(TEST_PREFIX + "student1", irisSession.getId()); - verifyNothingElseWasSentOverWebsocket(TEST_PREFIX + "student1", irisSession.getId()); } @Test @@ -277,7 +280,6 @@ void resendMessage() throws Exception { request.postWithResponseBody("/api/iris/sessions/" + irisSession.getId() + "/messages/" + irisMessage.getId() + "/resend", null, IrisMessage.class, HttpStatus.OK); await().until(() -> irisSessionRepository.findByIdWithMessagesElseThrow(irisSession.getId()).getMessages().size() == 2); verifyMessageWasSentOverWebsocket(TEST_PREFIX + "student1", irisSession.getId(), "Hello World"); - verifyNothingElseWasSentOverWebsocket(TEST_PREFIX + "student1", irisSession.getId()); } @Test @@ -302,7 +304,6 @@ void sendMessageRateLimitReached() throws Exception { HttpStatus.TOO_MANY_REQUESTS); verifyMessageWasSentOverWebsocket(TEST_PREFIX + "student2", irisSession.getId(), messageToSend1); verifyMessageWasSentOverWebsocket(TEST_PREFIX + "student2", irisSession.getId(), "Hello World"); - verifyNothingElseWasSentOverWebsocket(TEST_PREFIX + "student2", irisSession.getId()); } finally { ReflectionTestUtils.setField(irisRateLimitService, "rateLimit", previousRateLimit);