diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 96125848c361..987634ddf123 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -81,7 +81,6 @@ Prerequisites: ![](https://byob.yarr.is/ls1intum/Artemis/artemis-test4) ![](https://byob.yarr.is/ls1intum/Artemis/artemis-test5) ![](https://byob.yarr.is/ls1intum/Artemis/artemis-test6) -![](https://byob.yarr.is/ls1intum/Artemis/artemis-test9) ### Review Progress diff --git a/.github/workflows/testserver.yml b/.github/workflows/testserver.yml index 8c5bf90e6d7d..c011fc583f0c 100644 --- a/.github/workflows/testserver.yml +++ b/.github/workflows/testserver.yml @@ -95,17 +95,12 @@ jobs: matrix: include: # Commented out environments are not yet available and will be enabled in the future - - environment: artemis-test1.artemis.cit.tum.de label-identifier: artemis-test1 url: https://artemis-test1.artemis.cit.tum.de user: deployment hosts: artemis-test1.artemis.cit.tum.de folder: /opt/artemis - host_keys: | - artemis-test1.artemis.cit.tum.de ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDS+lFrN7hvzFjESxSQkmLbBdra9dWo33izxLRO2x5oTQwcbZA66Y3cQungIV460aQHwH+K0ALQuEc5EA7LegfFXo//t3kqXEALf0xRkO5tLnJEBpwnjpiOj8GFVIR8XOopKHf3zLo7/rmCPmdN0TKKigw9PcowB3Nf9TlpXvVtpkNTqnjwxfsLqvVjTf+8ji48Xe8zOhAH4zyJEc1KFM7XNdYYJPUctQyOoF+9QrTEW2GLYRkb2IqL8RDd09riUrjtsQJdoK21ATPC07j6XqGCgZxZQfmq4NlqEg2euQt45l0ZkVp3sQG70U0NkwKsLLhABWOzKDWvix7mJ/YDXCkk4Q8Badus1vIBAZYuTATfsEwXnQEnKTl8i4l7CE6U4PZLhwOgxcLBU9E3YKLjCsp8wfEV0n/wyoxXKUSH7Lb0jPQ4JMVUgNT5Rkdt0RE3YMWxVLrQNs6u33m2LrXt3lmiZsGpFa08RcjnSdFtHonL/CkwU00kckoMwRPgUniJKmM= - artemis-test1.artemis.cit.tum.de ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMk4f5/x4grLMOY9jQCp3SfSjF81iWYRCbXJHBMWzG3TbQq9d2zW8bGCak5TGwPCDnjfBPRRSF57LZJlNaf3wPE= - artemis-test1.artemis.cit.tum.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBGzlZUARYQBdq85FtCe4IALac1dSWoNsvQZmeCjPmS/ - environment: artemis-test2.artemis.cit.tum.de label-identifier: artemis-test2 @@ -113,10 +108,6 @@ jobs: user: deployment hosts: artemis-test2.artemis.cit.tum.de folder: /opt/artemis - host_keys: | - artemis-test2.artemis.cit.tum.de ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDUqNIPYUJBuItIfGzVu8jtpNWerFoqNq34cPU/+w5biwCrQA/RMIRt0H49ETc4vgc3rN85//FJ9j2x4jGfyS3jxj/ind6PX1QIBjGl09s2TFz53Z8OwcAw9iXJrtKqZGfxr8sbVfboDGa/kyr8E+YUJo+6bPusLQPSAJn8GUGgcNKHQSX7A4sEIeq2uU1RqGKDWJSxyECUxyubfNB6LCZd3ezEP1MPDnvhoF/5cEP4QxeTsVIkwIIAE8oVxNM7Ni8xqkl8sUM//SdzglFsK2gE5eSZ5OpQ5h/Cc3Oo1z7LnwlMwo8fGnhAcoUpDjuKD/2AdIhkyW0B4xUKbKVO94kVSuBUXErYqF4bHByGgjkzR0JTEwk5+shlUjoEA6DBxBO08CudJcTDUhk5+8fRwOzxfSTakr8sOfakgo7W6fBl3P4lHSdsd7VqKINcR3A9QYSXeiEeqliXnTkDSsZw4ux9JyuLle1DHPbTuH8f+vEosdxda+djm3FeijYTe4QS87k= - artemis-test2.artemis.cit.tum.de ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDRCF0zH3u38zVPNUhJI7zIDsXa3ONiU0GeCv/ybjawkh4alBPnlXtdH0cG1JPtR/Jz/gau92dcqiIFtqdDCDkg= - artemis-test2.artemis.cit.tum.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMV70ACU6GdLtf1MwUklNltC78UoOPsasZruYh0Ord6n - environment: artemis-test3.artemis.cit.tum.de label-identifier: artemis-test3 @@ -124,10 +115,6 @@ jobs: user: deployment hosts: artemis-test3.artemis.cit.tum.de folder: /opt/artemis - host_keys: | - artemis-test3.artemis.cit.tum.de ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC3zNbNkbYMUbNKCtVSa1GH7ovysowYQQHQK3owHbfjyKsaS6lTO2o16mMe3pu0+CwMdsJqBqn5Lt6M9HyCW74WwwWbOcUrcSqE/37hx5Ja2YzCwucTpvUCR3WVrmwBEYCoS5ZdQfFmyfVQtqjCCI5DdRk1wgY00eLoc8d6YOb6XnmMTs41WcyXFl8ffjhG3jMGlQILI8zhyZqnYB8HwadRAp8Oa6+DyGhOBAV5d3S0AZqjMKNoBGSAXCfs/nG3jBigxNCV7zeIjuhi2Ize/GY+gMcCrvWhjs/lXERII7RDIlcZogyN9+rGRX8X8okMoS1YONxzWPFb6XQajeDriESQ5txyHXpbFwcSLSU8DzleS9UZMah99knMs0Fyzu0q4rbCS1PtaAJfSOLjVgp67j3DNkXV+P5CZaSYI7hl377u0aTTHB5W3Myn7kXrNL2vjRk/mui+/Ds/+PPCJERWJAhCYp+CGj/itcOKPJqyfLL3ejzpqhRDzDTWCHU2cUnE2PE= - artemis-test3.artemis.cit.tum.de ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBF3JQ7fPlW+Rua4JXCl4Dska45h4PhClWvmcFQHGA4H0bLEG+lVXuw5yuRk9lDD88pvzStFewk9EbmJ8Sja0zKo= - artemis-test3.artemis.cit.tum.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGE67ADXnCgvbyJfqDAmSvegYwmCzfOyAUKBbJpwh7oU - environment: artemis-test4.artemis.cit.tum.de label-identifier: artemis-test4 @@ -135,10 +122,6 @@ jobs: user: deployment hosts: artemis-test4.artemis.cit.tum.de folder: /opt/artemis - host_keys: | - artemis-test4.artemis.cit.tum.de ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCwYFngAYWkI7yaf/6Ni81xCgWyh2gFlGPsUBq57iwROEQjfebTB8lWk2VfC3uCNXIEYB0IBQptPljRF2yQq+vUUeMerzXd5D/aeDmtS59f/z3W3ah3k3N7y5kFmBTGdvg53l+B34lQG6bEbB4McX8MhSUYdtAkp6Sc5EcxXiaDhcgGMCtXSMBJUrIGN+Is4AlWlqCGkL1KP+Y23rOaL3co+RppkcPDoKat8BZD2EbVBp9ZqqAVc+qIud/IMbTVZkhnaNoC0K33aoBLtauoTbYo/5kOlwFCDGM+NiNaXrOF5ANGJxX8WYoFXWVxxJFDVAm3x7ajZqydmkxWhPfUa9LIIEy2QbmeYvosfd8p/5STOQCHqE9dLHwjtin/vL1b0HmclIQUhrMVYMTeJNSCN9D2uRcU9U7Cz4qbX9s6/eCVjHfTkr+b0gvM1tm5OwZBaH1++aJj4JakACgwHZoRV08hmf6jGHVCoVStCOCe0TUT9enCMPs+Y+BFuoDWVlOLDi8= - artemis-test4.artemis.cit.tum.de ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHyKKZgX8bZ+k7N/B42LhBvJG2FcNhPL11q2L9FYDV/N2DAMHnBaxoruw+NdIaG0b/rfseUJKXPQiY6TMr0kOrk= - artemis-test4.artemis.cit.tum.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE9lVNwikkOcJb/7BdV200KcEtpisvhkOHMcKZeF3Tvb - environment: artemis-test5.artemis.cit.tum.de label-identifier: artemis-test5 @@ -146,10 +129,6 @@ jobs: user: deployment hosts: artemis-test5.artemis.cit.tum.de folder: /opt/artemis - host_keys: | - artemis-test5.artemis.cit.tum.de ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQD0NyNrw8dlJ99iGRSQ5IemSBpJyruNjFFFquiv8wsVRsBL6XPNySmZm0XWh8MjLxlL7DH3rUxXupaAnH6kQAAzt9gbWS4E+k21sfoKU0muf9rE1e7rUjnKgOm70b+6kpc7AS8HBAmzS2GuDzufPiyPYlUjozLhb2nAN685lT2v/Ofoe8tdWsR7AkB9pJE5P4NhAS/a4JujpyhO186F+SyT9k4X8FlfMi40qdBm1eIkg+eljVfbaIn0pKdoy/n8s0ODWu7vZpzNQxZb7qm5ROUjTqObSqsUA8oQv0JuWf4oudVLg3gxV1fEmEdAaWFt6W2mkiFaLeYTrhdtwD627WCyUPaSGe8yy8itEPsw2edfKZqb7s0+H6SlgvqR4rtKkbXs5gMG3Q2Ezj1C4SYrR/ME6oy6xt0zQEG7hiKIl2ArFZA6xRnhnqck8sTr+6oqRdNbKeErYroOYwKquNfuLHv1Fy2OpefMI+tjWSIFhDYDW2Y9d/ySPETyhHmZrn40Bk8= - artemis-test5.artemis.cit.tum.de ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBImSPJjIhJGsbUABhZfjM5vbc0UVC7JTDS1xwdIdJfAIEMo101EIS98C340VtiU4f6G2z59zpRB1Udptv1cxbPI= - artemis-test5.artemis.cit.tum.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHonMyIKiEHw7miwe5PJMDvWO/PwTpQpFYsvi2DUXYrO - environment: artemis-test6.artemis.cit.tum.de label-identifier: artemis-test6 @@ -158,9 +137,6 @@ jobs: hosts: artemis-test6.artemis.cit.tum.de folder: /opt/artemis host_keys: | - artemis-test6.artemis.cit.tum.de ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDKF/XYwHLiJ8JzYgcjxC9xRFrZqkLZz1sXa2PzJPTVzf+Ms58Emcor1/9Th+HgFtksEN2rsgPJackGBdigoDJn2n9ALzioWjIjdp4aH2Vl7wl91+ms2EWsAp1ZzywCFDYTQwqhmfztyS+SgKUrBGXJ+BB9rl115/0bt0wRCVhlI/QOWBp2renogzt/39BHEVNUp1dLeAReQmDjkiPZGyShkEVi1pODpIYoQ9KPcBlbT3/T5G3WaQDWZyt7J7vZa+nEI7rMRYcNF9RyqMs96dHmivputjg9ESNX9tH+3YonwCKdt2jCoV76VD31S3F7uK2pz5L7GdakM9n3CiT7K7msg7zFAPrxFoV3POpqWMc3NxEB7ZufQCL/VJ5mTZmj6jSUIRXsOdzZpgb6ZdqXd4cdWX56VY1YZwnXk9rxwxPDLyl3ugspvW9pmAJuk0/kltSq7j8w5toiclKgUte2CwrqOFdoj05m+1C1pUwCoeKxuV0ugHOS/M1fM64wikj9+cU= - artemis-test6.artemis.cit.tum.de ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI5x3Yh7ecWwh+oitXTMwRnjyVRwlikkIqvmpYb4TF6rgSxBAxfkiWHiEyTWS7eBtdhp/v0FmhJV9dsehZSxZYQ= - artemis-test6.artemis.cit.tum.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZdVOmY8K39PId1WPGMZaQwJvtDbGqJTdJX26YtDXgI #- environment: artemis-test7.artemis.cit.tum.de # label-identifier: artemis-test7 @@ -168,10 +144,6 @@ jobs: # user: deployment # hosts: artemis-test7.artemis.cit.tum.de # folder: /opt/artemis - # host_keys: | - # artemis-test7.artemis.cit.tum.de ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDPBbRDLfjS+OBEv+qRPyAqZGB4VzXporexFM1xzBRq6S+q5SSuik9uXJ+ZJE3m4jrXmU0S+41dRhPIVhjtzqQgRkFe4zBjbbinQJhVlb6u8PQp35+pgDFzMswELj3wv3FWeJSkCNivhyTe7R5ZF3KrzyuEzlgMmmuqogyaNwJeGapaCDxYIMKb6HokKVlwl2RVxPeT7wRLIwPF5bujCA3ZUGDPvVNJT2h0nX0u1vpAYIDK1+5G7Sod7OLhRO0C1T0WBF5bPRAZfFLP1eZMCeUjIY9Xsc+0TwRSBGDy1DA+hmrzD83BS2Juy2nEnSG3oC8J0DOfJ0hreTm/0EA4F1y5oz9x0qgnmVqtz/+R16OLCo8mEOmBSwVLeeQJKy75/NRMxoeQ+jl8GEvO8gwfgxbYwBg6nHQzAgslCTMxBCIWvufRLK7n9IAc0zQHDJEcn6o9dN4RvUkC8DnlUqW6C2RVfjqr1qiwa3pS5wDjLqD9ctzZzxwXK7tYyZIqKOsaj7k= - # artemis-test7.artemis.cit.tum.de ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOVPCPO7KIjhJs8ISnVLjyDyKIid42AIBylpyp6PIKCVwLM8GNVbdQtZEkzpLnR4tIXdf7fJvg9OyIu36Lf21po= - # artemis-test7.artemis.cit.tum.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMCmfbFQB7wbdaocZlWn45BYn8CaMfFHKuymHcLNq+JA #- environment: artemis-test8.artemis.cit.tum.de # label-identifier: artemis-test8 @@ -179,21 +151,13 @@ jobs: # user: deployment # hosts: artemis-test8.artemis.cit.tum.de # folder: /opt/artemis - # host_keys: | - # artemis-test8.artemis.cit.tum.de ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDAioy2yTY0p/deKM6j7gMJA4tj7NadooMcPjkMkIHCFhVSsUPlhvBC1KzBm/JyfiXN/3kFZE9bZXnYn8aor2j4eiPXGEl3H070bTVPYCBuz3KHD/1zKpo/j317bacYSZivDb9HQmaGKBezDlx4R7+lZoAloozqXOfQU2AVOLD14bn75Xfxe+uB8gjNy0bACgolleKOHDTMMkLPc2O6i2X6yOa8c/kgbGzXZXEq9/rB81ggw8TBcf1FXaCgSlQMjoO69rbNBliMtybMnsdMZxan79xBT35LvlQtRezCGzT5U9NNaPluxkWBMgGCfIaj6CG8tliw6IQY0oKkpVEdsMZkltEzbgZ39Fn/telz0G8RL/GDC/1ew0b0dfE0IknExp7XzFrVokrmPcfF5D11s+F8ZeIYdyOUyLP9gEgscethj9MNTpVwoeiCvMyzOvypMF8DHxKxs73cqJ5W/vRacMepSPwv/KJCT7geX8KjO4ABbQG7GToiZ6wQYsnvTjtcWPs= - # artemis-test8.artemis.cit.tum.de ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGxtzUJPdHMAIfaL4Dr8OLMySCNInARsqnNN3e0JqHG/Y6xCVDVF9ZTOGUqMO9woMhsyTU6EOo6KaVfIVrw7SEA= - # artemis-test8.artemis.cit.tum.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFN6fu1oNKX3xFE1pe6Ichl5XJ/eTYhihywVvkH0mWRF - - - environment: artemis-test9.artemis.cit.tum.de - label-identifier: artemis-test9 - url: https://artemis-test9.artemis.cit.tum.de - user: deployment - hosts: artemis-test9.artemis.cit.tum.de - folder: /opt/artemis - host_keys: | - artemis-test9.artemis.cit.tum.de ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDRDisk0MgHoxymMGadsBitagXKn0DGhlg9khTHyel1CjQeNNc+d/sf8JHabodmMsZo4UKlKnVHDL48mzJWuTiacB0YvWfYYM+WKGZ3Wyc5INr4hK4Yj632UMEqBdLZqF3SJA2jPQk2u1rO416TeYJfR7pJwM694HmtuFifqmMaugV7evug195KckL+mxwxghjkRLtaY0AB5l0f0S5obVWTkKNaNaSKRidJfUyv6kZkZn2zZACWqkCudsEAjT6ZFXWhjiQqglmHZwlX6pLjsI6kL+C6xd4S/yB4okh3Rb7RVWxbT/9YzOAWJzRu1+IGf1/5UeFUEqKL1B8TJ0yZA1xZAXtvmYecK+iBibmeTXBNN/TBsTjPrArfUPnWN3/C8nf0lMyce/iPbyYMmu1oiHo3NdLXGsHpk2+oLAWjY7edcF/WyXOp0ax4OCIZ/lMsbA8euNewEahfDGFyRXzj694uwog8zpMtANb4+Lxo26N8hgSBZGsb22vLCHMerS5dfCU= - artemis-test9.artemis.cit.tum.de ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBG3531IQdJVxkckhWGahHh4sb5REa3g+0UYMxGOKExj0Q/KxKFcfsu0rJ0vN4zKEfuZRHJouNcLQVVJfSDxqZF8= - artemis-test9.artemis.cit.tum.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDbS/SXTRdYBE/qVjRHNMPXXMegsjBZrnuK6oAQJJstZ + + #- environment: artemis-test9.artemis.cit.tum.de + # label-identifier: artemis-test9 + # url: https://artemis-test9.artemis.cit.tum.de + # user: deployment + # hosts: artemis-test9.artemis.cit.tum.de + # folder: /opt/artemis #- environment: artemis-test10.artemis.cit.tum.de # label-identifier: artemis-test10 @@ -201,10 +165,6 @@ jobs: # user: deployment # hosts: artemis-test10.artemis.cit.tum.de # folder: /opt/artemis - # host_keys: | - # artemis-test10.artemis.cit.tum.de ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC82ixfnlVeM94VjifcoGftJraCV71TMBD6/1lGEAvOAEkUckZCWjASRuyvzNvgJAeUYUe93oUfQnEdWRYrtvbWHtO/Oe0fZN0VQJorKnpFyuPOC363Z25Rc1PUAFX5lwlKBfeZkTvg1PVNE9o+yVLmeApRPLjUsF1aNrInbXXgzF4L2GgMgQvr6DW5WQQkwIrjJn4XlZV4nj6lheip+a194yD/4A05UJ/iHIEodzu5+UWVLn2HKZAbWO1VhhU75x5iuTtPASlbbsbq/3cDwSLrAMkI2Vyu4mXRNd4B/L6Js8ilwRe1iIaqnKeNswwfg/7TkCjCU+4sfdHnNc2iHx0vOayw/0pKEtn0L8SpJgBr5ncdIavRNsCZ1caRv44Ino2WWxq/sFFb4Wh2iXYqEbMhEKuBGqryzJF9veh0UTyOQXyuJBSJu4pZ/9YqG6F21girq2RFSWo+roZsnq+8LEkti4dGW22+yFvDgs3sqvTxgSVWIlQZt7CldVTvPyL/0+M= - # artemis-test10.artemis.cit.tum.de ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCICiCLtljuYwnBxGKurZjMuDsYFfSJ/5UD8gaDa1+CWPqmM4cFTniw+ORglDpaySlusPbXwll+K0JPkIm8E6+Y= - # artemis-test10.artemis.cit.tum.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAF8QXj0d2na/rBaVTIXfGu3HgtFppoE04Oj1Od2O3kD outputs: TS1: ${{ steps.filter.outputs.artemis-test1 || '' }} TS2: ${{ steps.filter.outputs.artemis-test2 || '' }} @@ -214,7 +174,7 @@ jobs: TS6: ${{ steps.filter.outputs.artemis-test6 || '' }} #TS7: ${{ steps.filter.outputs.artemis-test7 || '' }} #TS8: ${{ steps.filter.outputs.artemis-test8 || '' }} - TS9: ${{ steps.filter.outputs.artemis-test9 || '' }} + #TS9: ${{ steps.filter.outputs.artemis-test9 || '' }} #TS10: ${{ steps.filter.outputs.artemis-test10 || '' }} steps: - run: | @@ -266,7 +226,6 @@ jobs: DEPLOYMENT_USER: ${{ matrix.user }} DEPLOYMENT_HOSTS: ${{ matrix.hosts }} DEPLOYMENT_FOLDER: ${{ matrix.folder }} - DEPLOYMENT_HOST_PUBLIC_KEYS: ${{ matrix.host_keys }} GATEWAY_USER: "jump" GATEWAY_HOST: "gateway.artemis.in.tum.de:2010" GATEWAY_HOST_PUBLIC_KEY: "[gateway.artemis.in.tum.de]:2010 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKtTLiKRILjKZ+Qg4ReWKsG7mLDXkzHfeY5nalSQUNQ4" @@ -347,7 +306,6 @@ jobs: ssh-add - <<< $GATEWAY_SSH_KEY ssh-add - <<< $DEPLOYMENT_SSH_KEY cat - <<< $GATEWAY_HOST_PUBLIC_KEY >> ~/.ssh/known_hosts - cat - <<< $(sed 's/\\n/\n/g' <<< "$DEPLOYMENT_HOST_PUBLIC_KEYS") >> ~/.ssh/known_hosts - name: Deploy Artemis with Docker env: diff --git a/docker/postgres.yml b/docker/postgres.yml index 1e944e268f17..a4180a25bcc2 100644 --- a/docker/postgres.yml +++ b/docker/postgres.yml @@ -8,7 +8,7 @@ services: image: docker.io/library/postgres:16.1-alpine pull_policy: if_not_present user: postgres - command: ["postgres", "-c", "max_connections=200"] + command: ["postgres", "-c", "max_connections=10000"] volumes: - artemis-postgres-data:/var/lib/postgresql/data # DO NOT use this default file for production systems! diff --git a/docker/test-server-multi-node-postgresql-localci.yml b/docker/test-server-multi-node-postgresql-localci.yml new file mode 100644 index 000000000000..61348ae6c0e7 --- /dev/null +++ b/docker/test-server-multi-node-postgresql-localci.yml @@ -0,0 +1,139 @@ +# ---------------------------------------------------------------------------------------------------------------------- +# Setup for a multi-node test server with MySQL & LocalCI +# ---------------------------------------------------------------------------------------------------------------------- +# It is designed to take in a lot of environment variables to take in all the configuration of the test server. +# ---------------------------------------------------------------------------------------------------------------------- + +services: + artemis-app-node-1: + container_name: artemis-app-node-1 + extends: + file: ./artemis.yml + service: artemis-app + image: ghcr.io/ls1intum/artemis:${ARTEMIS_DOCKER_TAG:-latest} + depends_on: + postgres: + condition: service_healthy + jhipster-registry: + condition: service_healthy + activemq-broker: + condition: service_healthy + pull_policy: always + restart: always + group_add: + - ${DOCKER_GROUP_ID:-0} + env_file: + - ${ARTEMIS_ENV_FILE:-./artemis/config/prod-multinode.env} + - ${ARTEMIS_NODE_1_ENV_FILE:-./artemis/config/node1.env} + volumes: + - ${ARTEMIS_VOLUME_MOUNT:-./.docker-data/artemis-data}:/opt/artemis/data + - ${ARTEMIS_LEGAL_MOUNT:-./.docker-data/artemis-legal}:/opt/artemis/legal + - ${ARTEMIS_DATA_EXPORT_MOUNT:-./.docker-data/artemis-data-exports}:/opt/artemis/data-exports + - /var/run/docker.sock:/var/run/docker.sock + + artemis-app-node-2: + container_name: artemis-app-node-2 + extends: + file: ./artemis.yml + service: artemis-app + image: ghcr.io/ls1intum/artemis:${ARTEMIS_DOCKER_TAG:-latest} + depends_on: + postgres: + condition: service_healthy + jhipster-registry: + condition: service_healthy + activemq-broker: + condition: service_healthy + artemis-app-node-1: + condition: service_healthy + pull_policy: always + restart: always + group_add: + - ${DOCKER_GROUP_ID:-0} + env_file: + - ${ARTEMIS_ENV_FILE:-./artemis/config/prod-multinode.env} + - ${ARTEMIS_NODE_2_ENV_FILE:-./artemis/config/node2.env} + volumes: + - ${ARTEMIS_VOLUME_MOUNT:-./.docker-data/artemis-data}:/opt/artemis/data + - ${ARTEMIS_LEGAL_MOUNT:-./.docker-data/artemis-legal}:/opt/artemis/legal + - ${ARTEMIS_DATA_EXPORT_MOUNT:-./.docker-data/artemis-data-exports}:/opt/artemis/data-exports + - /var/run/docker.sock:/var/run/docker.sock + + artemis-app-node-3: + container_name: artemis-app-node-3 + extends: + file: ./artemis.yml + service: artemis-app + image: ghcr.io/ls1intum/artemis:${ARTEMIS_DOCKER_TAG:-latest} + depends_on: + postgres: + condition: service_healthy + jhipster-registry: + condition: service_healthy + activemq-broker: + condition: service_healthy + artemis-app-node-1: + condition: service_healthy + pull_policy: always + restart: always + group_add: + - ${DOCKER_GROUP_ID:-0} + env_file: + - ${ARTEMIS_ENV_FILE:-./artemis/config/prod-multinode.env} + - ${ARTEMIS_NODE_3_ENV_FILE:-./artemis/config/node3.env} + volumes: + - ${ARTEMIS_VOLUME_MOUNT:-./.docker-data/artemis-data}:/opt/artemis/data + - ${ARTEMIS_LEGAL_MOUNT:-./.docker-data/artemis-legal}:/opt/artemis/legal + - ${ARTEMIS_DATA_EXPORT_MOUNT:-./.docker-data/artemis-data-exports}:/opt/artemis/data-exports + - /var/run/docker.sock:/var/run/docker.sock + + jhipster-registry: + extends: + file: ./broker-registry.yml + service: jhipster-registry + networks: + - artemis + + activemq-broker: + extends: + file: ./broker-registry.yml + service: activemq-broker + networks: + - artemis + + postgres: + extends: + file: ./postgres.yml + service: postgres + restart: always + env_file: + - ${DATABASE_ENV_FILE:-./postgres/default.env} + volumes: + - ${DATABASE_VOLUME_MOUNT:-./.docker-data/artemis-postgres-data}:/var/lib/postgresql/data + + + nginx: + extends: + file: ./nginx.yml + service: nginx + depends_on: + artemis-app-node-1: + condition: service_started + artemis-app-node-2: + condition: service_started + artemis-app-node-3: + condition: service_started + restart: always + volumes: + - ./nginx/artemis-upstream-multi-node.conf:/etc/nginx/includes/artemis-upstream.conf:ro + - type: bind + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + target: "/certs/fullchain.pem" + - type: bind + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + target: "/certs/priv_key.pem" + +networks: + artemis: + driver: "bridge" + name: artemis diff --git a/docs/dev/setup.rst b/docs/dev/setup.rst index e3fb5be38e54..5f359ea0e891 100644 --- a/docs/dev/setup.rst +++ b/docs/dev/setup.rst @@ -26,7 +26,7 @@ following dependencies/tools on your machine: 1. `Java JDK `__: - We use Java (JDK 17) to develop and run the Artemis application + We use Java (JDK 21) to develop and run the Artemis application server, which is based on `Spring Boot `__. 2. `MySQL Database Server 8 `__, or `PostgreSQL `_: diff --git a/docs/dev/system-design/integrated-code-lifecycle/Integrated_Code_Lifecycle_Class_Diagram_Build_Job_Cancellation.png b/docs/dev/system-design/integrated-code-lifecycle/Integrated_Code_Lifecycle_Class_Diagram_Build_Job_Cancellation.png new file mode 100644 index 000000000000..bc348ad279e5 Binary files /dev/null and b/docs/dev/system-design/integrated-code-lifecycle/Integrated_Code_Lifecycle_Class_Diagram_Build_Job_Cancellation.png differ diff --git a/docs/dev/system-design/integrated-code-lifecycle/Integrated_Code_Lifecycle_Communication_Diagram_Cancellation.png b/docs/dev/system-design/integrated-code-lifecycle/Integrated_Code_Lifecycle_Communication_Diagram_Cancellation.png new file mode 100644 index 000000000000..d1b6ac7d3d59 Binary files /dev/null and b/docs/dev/system-design/integrated-code-lifecycle/Integrated_Code_Lifecycle_Communication_Diagram_Cancellation.png differ diff --git a/docs/dev/system-design/integrated-code-lifecycle/integrated-code-lifecycle-system-design.rst b/docs/dev/system-design/integrated-code-lifecycle/integrated-code-lifecycle-system-design.rst index 07a4ebe64b5d..5bd8975904d7 100644 --- a/docs/dev/system-design/integrated-code-lifecycle/integrated-code-lifecycle-system-design.rst +++ b/docs/dev/system-design/integrated-code-lifecycle/integrated-code-lifecycle-system-design.rst @@ -142,3 +142,84 @@ The ephemeral nature of Docker containers allows the ``BuildJobExecutionService` Finally, when the build ran through successfully, the ``SharedQueueProcessingService`` puts the build result into the result queue so it can then be processed by the CI Management. If there were any errors, the ``BuildJobManagementService`` stops the Docker container and ``SharedQueueProcessingService`` relays the exception message to the CI Management via the result queue. +Hazelcast Distributed Data Structures +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The integration employs various Hazelcast distributed data structures to streamline the processing of build jobs across the network. +These structures include the ``buildJobQueue``, an ``IQueue`` used for queuing all build jobs awaiting processing; +the ``processingJobs``, an ``IMap`` tracking build jobs currently under processing; +and the ``canceledBuildJobsTopic``, an ``ITopic`` utilized to signal build agents to cancel specific build jobs. + +Here we can see an overview of all Hazelcast data structures used in Local CI and their purpose: + ++----------------------------+------------+-------------------------------------------------------------------------+ +| Name | Type | Description | ++============================+============+=========================================================================+ +| ``buildJobQueue`` | ``IQueue`` | Queue for all build jobs that are waiting to be processed. | ++----------------------------+------------+-------------------------------------------------------------------------+ +| ``processingJobs`` | ``IMap`` | Map including all build jobs that are currently being processed. | ++----------------------------+------------+-------------------------------------------------------------------------+ +| ``buildAgentInformation`` | ``IMap`` | Map containing information on each build agent. | ++----------------------------+------------+-------------------------------------------------------------------------+ +| ``canceledBuildsTopic`` | ``ITopic`` | Topic to order build agents to cancel a specific build job. | ++----------------------------+------------+-------------------------------------------------------------------------+ +| ``buildJobResultQueue`` | ``IQueue`` | Queue containing the results produced by the build job execution. | ++----------------------------+------------+-------------------------------------------------------------------------+ +| ``dockerImageCleanupInfo`` | ``IMap`` | Map containing information about the last date of use of Docker images. | ++----------------------------+------------+-------------------------------------------------------------------------+ + + +This approach ensures the efficient synchronization of build job processing. +The CI Management also provides REST endpoints and supports WebSocket connections. +These endpoints and real-time connections allow users to monitor and manage both queued and running jobs along with the build agents themselves, +enhancing the system's operability and user interaction capabilities. +Additionally, the ``processingJobs``, ``buildJobQueue`` and ``canceledBuildJobsTopic`` are enhanced with Hazelcast ``EntryListener``, ``ItemListener`` and ``MessageListener``, enabling immediate detection of changes such as additions, removals, and updates. + +Build Job Cancellation +^^^^^^^^^^^^^^^^^^^^^^ + +The build job cancellation mechanism is a crucial feature of the CI system. +It allows users to cancel build jobs that are taking too long or are no longer needed. +The following diagram shows an overview of the components in the build job cancellation mechanism: + +.. figure:: /dev/system-design/integrated-code-lifecycle/Integrated_Code_Lifecycle_Class_Diagram_Build_Job_Cancellation.png + :align: center + :width: 800 + :alt: Build Job Cancellation + + + Build Job Cancellation Class Diagram + +The build job cancellation mechanism is implemented using Hazelcast's distributed data structures. +As shown in the `Build Job Cancellation Class Diagram` we have services such as the ``SharedQueueManagementService`` that run on the core Artemis node and are responsible for managing Hazelcast's distributed datatypes. +However, the real processing of each build job runs independently on each build agent. Therefore we have separate services such as the ``SharedQueueProcessingService`` in a build agent package . + +The processing of build jobs gets triggered by the insertion or removal of jobs from the ``buildJobQueue``. +The queue listener in the ``SharedQueueProcessingService`` is then responsible for invoking the ``checkAvailabilityAndProcessNextBuild()`` method. +If a build agent has available threads, the job is moved from the ``buildJobQueue`` to the ``processingJobs``. +Subsequently, the ``processBuild(LocalCIBuildJobQueueItem buildJob)`` method is called, which in turn invokes ``executeBuildJob(LocalCIBuildJobQueueItem buildJob)`` in the ``BuildJobManagementService``. +This method is tasked with asynchronously executing the build job in the ``BuildJobExecutionService``, by creating a future. +Upon creating the new future, it is added to a local ``Map> runningFutures``, which is unique to each build agent and contains only the futures of that agent. +This future is then processed, with outcomes such as completion, failure, or cancellation being sent back and managed within the ``processBuild(...)`` method. +This includes removing the build job from the ``processingJobs`` and initiating a new cycle by calling ``checkAvailabilityAndProcessNextBuild()``. + +.. figure:: /dev/system-design/integrated-code-lifecycle/Integrated_Code_Lifecycle_Communication_Diagram_Cancellation.png + :align: center + :width: 800 + :alt: Build Job Cancellation + + Build Job Cancellation Communication Diagram for a Processing Build Job + + +The above Communication Diagram sets the stage for understanding the cancellation mechanism. +Upon receiving a cancellation request through the ``@DeleteMapping("cancel-job/{buildJobId}")`` endpoint in either the ``AdminBuildJobQueueResource`` or ``BuildJobQueueResource``, +the ``cancelBuildJob(String buildJobId)`` method within the ``SharedQueueManagementService`` is activated. +Initially, it checks whether the build job is queued but not yet in process. If so, it is simply removed from the ``buildJobQueue``. +The process becomes more complex for jobs already in process, involving the ``canceledBuildJobsTopic`` ``ITopic```. +A cancellation message for the build job ID is broadcast to all build agents, with the ``BuildJobManagementService`` listening for these messages via Hazelcast's ``MessageListener``. +Every build agent then checks its ``runningFutures`` map to see if it holds the specific build job future. +Should a build agent currently process the specified job, it executes ``cancelBuildJob(String buildJobId)`` within the ``BuildJobManagementService``. +This step involves canceling the future and recording the job ID in ``Set cancelledBuildJobs``. +The cancellation triggers an Exception which is caught and managed in ``processBuild(...)`` within the ``SharedQueueProcessingService``. +The build status is set to ``CANCELLED``, the job is removed from ``processingJobs``, and the process reinitialized with ``checkAvailabilityAndProcessNextBuild()``. +Modifications to ``processingJobs`` trigger WebSocket updates, consequently removing the canceled job from the client's build queue display. diff --git a/src/main/java/de/tum/in/www1/artemis/aop/logging/LoggingAspect.java b/src/main/java/de/tum/in/www1/artemis/aop/logging/LoggingAspect.java index 09da3398796e..bf77bcec2ac0 100644 --- a/src/main/java/de/tum/in/www1/artemis/aop/logging/LoggingAspect.java +++ b/src/main/java/de/tum/in/www1/artemis/aop/logging/LoggingAspect.java @@ -15,7 +15,7 @@ /** * Aspect for logging execution of service and repository Spring components. - * + *

* By default, it only runs with the "dev" profile. */ @Aspect @@ -60,7 +60,7 @@ public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { } if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT))) { - log.error("Exception in {}.{}() with cause = \'{}\' and exception = \'{}\'", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), + log.error("Exception in {}.{}() with cause = '{}' and exception = '{}'", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), e.getCause() != null ? e.getCause() : "NULL", e.getMessage(), e); } else { diff --git a/src/main/java/de/tum/in/www1/artemis/config/LocaleConfiguration.java b/src/main/java/de/tum/in/www1/artemis/config/LocaleConfiguration.java index 903fff48560d..165bade4c67a 100644 --- a/src/main/java/de/tum/in/www1/artemis/config/LocaleConfiguration.java +++ b/src/main/java/de/tum/in/www1/artemis/config/LocaleConfiguration.java @@ -18,9 +18,7 @@ public class LocaleConfiguration implements WebMvcConfigurer { @Bean public LocaleResolver localeResolver() { - AngularCookieLocaleResolver cookieLocaleResolver = new AngularCookieLocaleResolver(); - cookieLocaleResolver.setCookieName("NG_TRANSLATE_LANG_KEY"); - return cookieLocaleResolver; + return new AngularCookieLocaleResolver("NG_TRANSLATE_LANG_KEY"); } @Override diff --git a/src/main/java/de/tum/in/www1/artemis/config/SecurityConfiguration.java b/src/main/java/de/tum/in/www1/artemis/config/SecurityConfiguration.java index 891e5e73dcef..ff2afc9583c0 100644 --- a/src/main/java/de/tum/in/www1/artemis/config/SecurityConfiguration.java +++ b/src/main/java/de/tum/in/www1/artemis/config/SecurityConfiguration.java @@ -163,41 +163,79 @@ public RoleHierarchy roleHierarchy() { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // @formatter:off http + // Disables CSRF (Cross-Site Request Forgery) protection; useful in stateless APIs where the token management is unnecessary. .csrf(AbstractHttpConfigurer::disable) + // Adds a CORS (Cross-Origin Resource Sharing) filter before the username/password authentication to handle cross-origin requests. .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class) + // Configures exception handling with a custom entry point and access denied handler for authentication issues. .exceptionHandling(handler -> handler.authenticationEntryPoint(problemSupport).accessDeniedHandler(problemSupport)) + // Adds a custom filter for Single Page Applications (SPA), i.e. the client, after the basic authentication filter. .addFilterAfter(new SpaWebFilter(), BasicAuthenticationFilter.class) + // Configures security headers. .headers(headers -> headers + // Sets Content Security Policy (CSP) directives to prevent XSS attacks. .contentSecurityPolicy(csp -> csp.policyDirectives("script-src 'self' 'unsafe-inline' 'unsafe-eval'")) + // Prevents the website from being framed, avoiding clickjacking attacks. .frameOptions(HeadersConfigurer.FrameOptionsConfig::deny) + // Sets Referrer Policy to limit the amount of referrer information sent with requests. .referrerPolicy(referrer -> referrer.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)) - .httpStrictTransportSecurity((HeadersConfigurer.HstsConfig::disable)) // this is already configured using nginx + // Disables HTTP Strict Transport Security as it is managed at the reverse proxy level (typically nginx). + .httpStrictTransportSecurity((HeadersConfigurer.HstsConfig::disable)) + // Defines Permissions Policy to restrict what features the browser is allowed to use. .permissionsPolicy(permissions -> permissions.policy("camera=(), fullscreen=(*), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), sync-xhr=()"))) + // Configures sessions to be stateless; appropriate for REST APIs where no session is required. .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .authorizeHttpRequests(requests -> requests - .requestMatchers("/", "/index.html", "/public/**").permitAll() - .requestMatchers("/*.js", "/*.css", "/*.map", "/*.json").permitAll() - .requestMatchers("/manifest.webapp", "/robots.txt").permitAll() - .requestMatchers("/content/**", "/i18n/*.json", "/logo/*").permitAll() - .requestMatchers("/management/info", "/management/health").permitAll() - .requestMatchers("/api/admin/**").hasAuthority(Role.ADMIN.getAuthority()) - .requestMatchers("/api/public/**").permitAll() - .requestMatchers("/websocket/**").permitAll() - .requestMatchers("/.well-known/jwks.json").permitAll() - .requestMatchers("/git/**").permitAll() - .requestMatchers("/management/prometheus/**").access((authentication, context) -> new AuthorizationDecision(monitoringIpAddresses.contains(context.getRequest().getRemoteAddr()))) - .requestMatchers("/**").authenticated() + // Configures authorization for various URL patterns. The patterns are considered in order. + .authorizeHttpRequests(requests -> { + requests + // Client related URLs and publicly accessible information (allowed for everyone). + .requestMatchers("/", "/index.html", "/public/**").permitAll() + .requestMatchers("/*.js", "/*.css", "/*.map", "/*.json").permitAll() + .requestMatchers("/manifest.webapp", "/robots.txt").permitAll() + .requestMatchers("/content/**", "/i18n/*.json", "/logo/*").permitAll() + // Information and health endpoints do not need authentication + .requestMatchers("/management/info", "/management/health").permitAll() + // Admin area requires specific authority. + .requestMatchers("/api/admin/**").hasAuthority(Role.ADMIN.getAuthority()) + // Publicly accessible API endpoints (allowed for everyone). + .requestMatchers("/api/public/**").permitAll() + // Websocket and other specific endpoints allowed without authentication. + .requestMatchers("/websocket/**").permitAll() + .requestMatchers("/.well-known/jwks.json").permitAll() + // Prometheus endpoint protected by IP address. + .requestMatchers("/management/prometheus/**").access((authentication, context) -> new AuthorizationDecision(monitoringIpAddresses.contains(context.getRequest().getRemoteAddr()))); + + // LocalVC related URLs: LocalVCPushFilter and LocalVCFetchFilter handle authentication on their own + if (profileService.isLocalVcsActive()) { + requests.requestMatchers("/git/**").permitAll(); + } + + // All other requests must be authenticated. Additional authorization happens on the endpoints themselves. + requests.requestMatchers("/**").authenticated(); + } ) + // Applies additional configurations defined in a custom security configurer adapter. .with(securityConfigurerAdapter(), configurer -> configurer.configure(http)); + // Enable HTTP Basic authentication so that people can authenticate using username and password against the server's REST API + // FIXME: This breaks LocalCI buildagents + //.httpBasic(Customizer.withDefaults()); // @formatter:on + // Conditionally adds configuration for LTI if it is active. if (profileService.isLtiActive()) { http.with(new CustomLti13Configurer(), configurer -> configurer.configure(http)); } + // Builds and returns the SecurityFilterChain. return http.build(); } + /** + * Creates and returns a JWTConfigurer instance. This configurer is responsible for integrating JWT-based authentication + * into the Spring Security filter chain. It configures how the security framework handles JWTs for authorizing requests. + * + * @return JWTConfigurer configured with a token provider that generates and validates JWT tokens. + */ private JWTConfigurer securityConfigurerAdapter() { return new JWTConfigurer(tokenProvider); } diff --git a/src/main/java/de/tum/in/www1/artemis/config/audit/AuditEventConverter.java b/src/main/java/de/tum/in/www1/artemis/config/audit/AuditEventConverter.java index 214644f35062..830862e9b86b 100644 --- a/src/main/java/de/tum/in/www1/artemis/config/audit/AuditEventConverter.java +++ b/src/main/java/de/tum/in/www1/artemis/config/audit/AuditEventConverter.java @@ -76,7 +76,7 @@ public Map convertDataToStrings(Map data) { results.put("remoteAddress", authenticationDetails.getRemoteAddress()); results.put("sessionId", authenticationDetails.getSessionId()); } - else if (object instanceof Pair authenticationPair) { + else if (object instanceof Pair authenticationPair) { results.put(authenticationPair.getFirst().toString(), authenticationPair.getSecond().toString()); } else { diff --git a/src/main/java/de/tum/in/www1/artemis/config/migration/DatabaseMigration.java b/src/main/java/de/tum/in/www1/artemis/config/migration/DatabaseMigration.java index 22c15f34ff51..3f4fcebffd33 100644 --- a/src/main/java/de/tum/in/www1/artemis/config/migration/DatabaseMigration.java +++ b/src/main/java/de/tum/in/www1/artemis/config/migration/DatabaseMigration.java @@ -28,16 +28,16 @@ class MigrationPath { /** The version from which migration can start, typically the last version before a major update. */ - Semver requiredVersion; // e.g. 5.12.9 + final Semver requiredVersion; // e.g. 5.12.9 /** The upgrade version to which the database can be migrated. */ - Semver upgradeVersion; // e.g. 6.0.0 --> this is also the target version + final Semver upgradeVersion; // e.g. 6.0.0 --> this is also the target version /** The next upgrade version before another major update is needed. */ - Semver nextUpgradeVersion; // e.g. 7.0.0 + final Semver nextUpgradeVersion; // e.g. 7.0.0 /** The error message to be displayed if the migration cannot proceed due to version incompatibility. */ - String errorMessage; + final String errorMessage; /** * Constructs a MigrationPath instance by defining the required version for the migration, diff --git a/src/main/java/de/tum/in/www1/artemis/config/migration/MigrationService.java b/src/main/java/de/tum/in/www1/artemis/config/migration/MigrationService.java index 6a1f4b7b5301..803d10bdcc91 100644 --- a/src/main/java/de/tum/in/www1/artemis/config/migration/MigrationService.java +++ b/src/main/java/de/tum/in/www1/artemis/config/migration/MigrationService.java @@ -129,7 +129,7 @@ public boolean checkIntegrity(SortedMap entryMap) { List entryList = entryMap.values().stream().toList(); if (!entryList.isEmpty()) { int startIndex = 1; - MigrationEntry baseEntry = entryList.get(0); + MigrationEntry baseEntry = entryList.getFirst(); // Make sure the base date is not null. If it is, it was already caught and logged above. while (!StringUtils.hasLength(baseEntry.date()) && startIndex < entryList.size()) { baseEntry = entryList.get(startIndex); diff --git a/src/main/java/de/tum/in/www1/artemis/config/websocket/WebsocketConfiguration.java b/src/main/java/de/tum/in/www1/artemis/config/websocket/WebsocketConfiguration.java index 4923192c7270..9e8c0400b7bb 100644 --- a/src/main/java/de/tum/in/www1/artemis/config/websocket/WebsocketConfiguration.java +++ b/src/main/java/de/tum/in/www1/artemis/config/websocket/WebsocketConfiguration.java @@ -149,8 +149,8 @@ protected void configureMessageBroker(@NotNull MessageBrokerRegistry config) { * Create a TCP client that will connect to the broker defined in the config. * If multiple brokers are configured, the client will connect to the first one and fail over to the next one in case a broker goes down. * If the last broker goes down, the first one is retried. - * Also see https://github.com/spring-projects/spring-framework/issues/17057 and - * https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#websocket-stomp-handle-broker-relay-configure + * Also see ... and + * ... * * @return a TCP client with a round-robin use */ diff --git a/src/main/java/de/tum/in/www1/artemis/config/websocket/WebsocketSecurityConfiguration.java b/src/main/java/de/tum/in/www1/artemis/config/websocket/WebsocketSecurityConfiguration.java index 0b3dcb77f909..7f90585bacba 100644 --- a/src/main/java/de/tum/in/www1/artemis/config/websocket/WebsocketSecurityConfiguration.java +++ b/src/main/java/de/tum/in/www1/artemis/config/websocket/WebsocketSecurityConfiguration.java @@ -2,20 +2,24 @@ import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; +import org.springframework.messaging.Message; import org.springframework.messaging.simp.SimpMessageType; -import org.springframework.security.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry; -import org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.config.annotation.web.socket.EnableWebSocketSecurity; +import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager; import de.tum.in.www1.artemis.security.Role; @Profile(PROFILE_CORE) @Configuration -public class WebsocketSecurityConfiguration extends AbstractSecurityWebSocketMessageBrokerConfigurer { +@EnableWebSocketSecurity +public class WebsocketSecurityConfiguration { - @Override - protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) { + @Bean + AuthorizationManager> authorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) { messages.nullDestMatcher().authenticated().simpDestMatchers("/topic").hasAuthority(Role.ADMIN.getAuthority()) // matches any destination that starts with /topic/ // (i.e. cannot send messages directly to /topic/) @@ -26,5 +30,6 @@ protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) .simpTypeMatchers(SimpMessageType.MESSAGE, SimpMessageType.SUBSCRIBE).denyAll() // catch all .anyMessage().denyAll(); + return messages.build(); } } diff --git a/src/main/java/de/tum/in/www1/artemis/domain/ComplaintResponse.java b/src/main/java/de/tum/in/www1/artemis/domain/ComplaintResponse.java index 8aeafc1c0c3c..26d52103f7b8 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/ComplaintResponse.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/ComplaintResponse.java @@ -18,7 +18,7 @@ /** * A ComplaintResponse. - * + *

* The createdDate in {@link AbstractAuditingEntity#createdDate} has a special meaning in this entity as it is used to calculate the lock status. See also * {@link ComplaintResponse#isCurrentlyLocked()} */ diff --git a/src/main/java/de/tum/in/www1/artemis/domain/Course.java b/src/main/java/de/tum/in/www1/artemis/domain/Course.java index 334d4c259f69..d7563c8884ef 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/Course.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/Course.java @@ -924,7 +924,7 @@ public void validateStartAndEndDate() { *

  • and the start and end date of the enrollment is before the end date of the course.
  • * * - * @throws BadRequestAlertException + * @throws BadRequestAlertException if the enrollment period is invalid */ public void validateEnrollmentStartAndEndDate() { if (getEnrollmentStartDate() == null || getEnrollmentEndDate() == null) { @@ -960,7 +960,7 @@ public void validateEnrollmentStartAndEndDate() { *
  • and the end date for unenrollment is not after the end date of the course.
  • * * - * @throws BadRequestAlertException + * @throws BadRequestAlertException if the unenrollment end date is invalid */ public void validateUnenrollmentEndDate() { if (getUnenrollmentEndDate() == null) { diff --git a/src/main/java/de/tum/in/www1/artemis/domain/Exercise.java b/src/main/java/de/tum/in/www1/artemis/domain/Exercise.java index 3a7614774466..2e7ed9bad8e8 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/Exercise.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/Exercise.java @@ -606,7 +606,7 @@ public Submission findAppropriateSubmissionByResults(Set submissions if (!submissionsWithRatedResult.isEmpty()) { if (submissionsWithRatedResult.size() == 1) { - return submissionsWithRatedResult.get(0); + return submissionsWithRatedResult.getFirst(); } else { // this means we have more than one submission, we want the one with the last submission date @@ -621,7 +621,7 @@ else if (!submissionsWithUnratedResult.isEmpty()) { return null; } if (submissionsWithUnratedResult.size() == 1) { - return submissionsWithUnratedResult.get(0); + return submissionsWithUnratedResult.getFirst(); } else { // this means with have more than one submission, we want the one with the last submission date // make sure that submissions without submission date do not lead to null pointer exception in the comparison @@ -630,7 +630,7 @@ else if (!submissionsWithUnratedResult.isEmpty()) { } else if (!submissionsWithoutResult.isEmpty()) { if (submissionsWithoutResult.size() == 1) { - return submissionsWithoutResult.get(0); + return submissionsWithoutResult.getFirst(); } else { // this means with have more than one submission, we want the one with the last submission date // make sure that submissions without submission date do not lead to null pointer exception in the comparison diff --git a/src/main/java/de/tum/in/www1/artemis/domain/GradeStep.java b/src/main/java/de/tum/in/www1/artemis/domain/GradeStep.java index 19bd6312273a..72c5ef83290f 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/GradeStep.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/GradeStep.java @@ -149,7 +149,7 @@ public boolean checkValidity() { /** * Parses the {@link #gradeName} as a number in order to use it in grade and bonus calculations. * Accepts both "," and "." as decimal separators. - * + *

    * Does not throw exception, returns null on failure. * * @return {@link Double} value corresponding to the {@link #gradeName} or null if it is not parseable or null. diff --git a/src/main/java/de/tum/in/www1/artemis/domain/LearningObject.java b/src/main/java/de/tum/in/www1/artemis/domain/LearningObject.java index 6b708b9b866b..bfc783a3c02a 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/LearningObject.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/LearningObject.java @@ -11,7 +11,7 @@ public interface LearningObject { /** * Whether the participant has completed the object * - * @param user + * @param user the user to check * @return True if completed, else false */ boolean isCompletedFor(User user); @@ -19,7 +19,7 @@ public interface LearningObject { /** * Get the date when the object has been completed by the participant * - * @param user + * @param user the user to retrieve the date for * @return The datetime when the object was first completed or null */ Optional getCompletionDate(User user); diff --git a/src/main/java/de/tum/in/www1/artemis/domain/StaticCodeAnalysisDefaultCategory.java b/src/main/java/de/tum/in/www1/artemis/domain/StaticCodeAnalysisDefaultCategory.java index 8a9d894499d8..cb0ec9d54534 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/StaticCodeAnalysisDefaultCategory.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/StaticCodeAnalysisDefaultCategory.java @@ -8,45 +8,7 @@ /** * Read-only POJO for storing static code analysis configurations initialized at start-up of Artemis */ -public class StaticCodeAnalysisDefaultCategory { - - private final String name; - - private final Double penalty; - - private final Double maxPenalty; - - private final CategoryState state; - - private final List categoryMappings; - - public StaticCodeAnalysisDefaultCategory(String name, Double penalty, Double maxPenalty, CategoryState state, List categoryMappings) { - this.name = name; - this.penalty = penalty; - this.maxPenalty = maxPenalty; - this.state = state; - this.categoryMappings = categoryMappings; - } - - public String getName() { - return name; - } - - public Double getPenalty() { - return penalty; - } - - public Double getMaxPenalty() { - return maxPenalty; - } - - public CategoryState getState() { - return state; - } - - public List getCategoryMappings() { - return categoryMappings; - } +public record StaticCodeAnalysisDefaultCategory(String name, Double penalty, Double maxPenalty, CategoryState state, List categoryMappings) { public record CategoryMapping(StaticCodeAnalysisTool tool, String category) { } diff --git a/src/main/java/de/tum/in/www1/artemis/domain/Submission.java b/src/main/java/de/tum/in/www1/artemis/domain/Submission.java index c1eaa80a6c90..174d7440eebc 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/Submission.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/Submission.java @@ -131,7 +131,7 @@ public Long getDurationInMinutes() { @JsonIgnore public Result getLatestResult() { if (results != null && !results.isEmpty()) { - return results.get(results.size() - 1); + return results.getLast(); } return null; } @@ -233,7 +233,7 @@ public Result getManualResultsById(long resultId) { @JsonIgnore public Result getFirstResult() { if (results != null && !results.isEmpty()) { - return results.get(0); + return results.getFirst(); } return null; } @@ -247,7 +247,7 @@ public Result getFirstResult() { @JsonIgnore public Result getFirstManualResult() { if (results != null && !results.isEmpty()) { - return this.getManualResults().get(0); + return this.getManualResults().getFirst(); } return null; } diff --git a/src/main/java/de/tum/in/www1/artemis/domain/competency/CompetencyProgress.java b/src/main/java/de/tum/in/www1/artemis/domain/competency/CompetencyProgress.java index 84e481ace0c7..e2fbabf6ef66 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/competency/CompetencyProgress.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/competency/CompetencyProgress.java @@ -119,7 +119,7 @@ public String toString() { /** * This class is used to create a composite primary key (user_id, competency_id). - * See also https://www.baeldung.com/spring-jpa-embedded-method-parameters + * See also ... */ @Embeddable public static class CompetencyUserId implements Serializable { diff --git a/src/main/java/de/tum/in/www1/artemis/domain/enumeration/TextAssessmentEventType.java b/src/main/java/de/tum/in/www1/artemis/domain/enumeration/TextAssessmentEventType.java index 8434d51e1ebb..a112cf214a03 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/enumeration/TextAssessmentEventType.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/enumeration/TextAssessmentEventType.java @@ -5,7 +5,7 @@ * Enumerates the names of events that are tracked when users interact with the assessment * system in text exercises * More detailed info in the documentation: - * https://docs.artemis.cit.tum.de/dev/setup/#configure-text-assessment-analytics-service + * ... */ public enum TextAssessmentEventType { ADD_FEEDBACK_AUTOMATICALLY_SELECTED_BLOCK, ADD_FEEDBACK_MANUALLY_SELECTED_BLOCK, DELETE_FEEDBACK, EDIT_AUTOMATIC_FEEDBACK, SUBMIT_ASSESSMENT, ASSESS_NEXT_SUBMISSION diff --git a/src/main/java/de/tum/in/www1/artemis/domain/hestia/ProgrammingExerciseSolutionEntry.java b/src/main/java/de/tum/in/www1/artemis/domain/hestia/ProgrammingExerciseSolutionEntry.java index 23d38f9acc52..2c4000535a10 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/hestia/ProgrammingExerciseSolutionEntry.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/hestia/ProgrammingExerciseSolutionEntry.java @@ -20,7 +20,7 @@ * If it encompasses the addition of an entire file, previousLine will be null. * If it deletes an entire file, line will be null. * previousLine and line will be different when there are other changes higher up in the file. - * + *

    * Example: * A print statement gets changed: * diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/message/IrisMessageContent.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/message/IrisMessageContent.java index edded2966d67..4d7f7c5bed37 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/message/IrisMessageContent.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/iris/message/IrisMessageContent.java @@ -6,9 +6,10 @@ import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; -import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import de.tum.in.www1.artemis.domain.DomainObject; diff --git a/src/main/java/de/tum/in/www1/artemis/domain/lecture/ExerciseUnit.java b/src/main/java/de/tum/in/www1/artemis/domain/lecture/ExerciseUnit.java index 555ae363509b..2ea12a990f3c 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/lecture/ExerciseUnit.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/lecture/ExerciseUnit.java @@ -5,7 +5,6 @@ import java.util.Set; import jakarta.persistence.*; -import jakarta.persistence.Entity; import org.hibernate.Hibernate; import org.hibernate.annotations.*; diff --git a/src/main/java/de/tum/in/www1/artemis/domain/lecture/LectureUnitCompletion.java b/src/main/java/de/tum/in/www1/artemis/domain/lecture/LectureUnitCompletion.java index 52616b0d352d..ca245810d622 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/lecture/LectureUnitCompletion.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/lecture/LectureUnitCompletion.java @@ -86,7 +86,7 @@ public int hashCode() { /** * This class is used to create a composite primary key (user_id, lecture_unit_id). - * See also https://www.baeldung.com/spring-jpa-embedded-method-parameters + * See also ... */ @Embeddable @SuppressWarnings("unused") diff --git a/src/main/java/de/tum/in/www1/artemis/domain/notification/SingleUserNotificationFactory.java b/src/main/java/de/tum/in/www1/artemis/domain/notification/SingleUserNotificationFactory.java index 616b3917c436..d10f10a39490 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/notification/SingleUserNotificationFactory.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/notification/SingleUserNotificationFactory.java @@ -323,11 +323,9 @@ public static SingleUserNotification createNotification(Conversation conversatio throw new IllegalArgumentException("No user provided for notification"); } SingleUserNotification notification = switch (notificationType) { - case CONVERSATION_CREATE_ONE_TO_ONE_CHAT -> { - // text is null because the notification is not shown - yield new SingleUserNotification(user, title, null, false, createPlaceholdersConversationCreateOneToOneChat()) + case CONVERSATION_CREATE_ONE_TO_ONE_CHAT -> // text is null because the notification is not shown + new SingleUserNotification(user, title, null, false, createPlaceholdersConversationCreateOneToOneChat()) .transientAndStringTarget(createConversationCreationTarget(conversation, conversation.getCourse().getId())); - } case CONVERSATION_CREATE_GROUP_CHAT, CONVERSATION_ADD_USER_GROUP_CHAT -> { String[] placeholders = createPlaceholdersForUserGroupChat(conversation.getCourse().getTitle(), responsibleForAction.getName()); yield new SingleUserNotification(user, title, CONVERSATION_ADD_USER_GROUP_CHAT_TEXT, true, placeholders) diff --git a/src/main/java/de/tum/in/www1/artemis/domain/participation/Participation.java b/src/main/java/de/tum/in/www1/artemis/domain/participation/Participation.java index 3fe500537713..48004b05978f 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/participation/Participation.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/participation/Participation.java @@ -252,7 +252,7 @@ private Result findLatestResult(boolean filterIllegalResults) { if (sortedResultsWithCompletionDate.isEmpty()) { return null; } - return sortedResultsWithCompletionDate.get(0); + return sortedResultsWithCompletionDate.getFirst(); } /** diff --git a/src/main/java/de/tum/in/www1/artemis/domain/participation/ProgrammingExerciseStudentParticipation.java b/src/main/java/de/tum/in/www1/artemis/domain/participation/ProgrammingExerciseStudentParticipation.java index 9e053de8693f..f68baba086e2 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/participation/ProgrammingExerciseStudentParticipation.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/participation/ProgrammingExerciseStudentParticipation.java @@ -34,7 +34,7 @@ public class ProgrammingExerciseStudentParticipation extends StudentParticipatio * Defines if the participation is locked, i.e. if the student can currently not make any submissions. * This takes into account: the start date of the exercise (or the exam), the (individual) due date, and the lock repository policy. * Course exercise practice repositories and instructor exam test run repositories will never be locked. - * + *

    * Important: this boolean flag must only be used for course programming exercises and is irrelevant for exam programming exercises!!! * */ diff --git a/src/main/java/de/tum/in/www1/artemis/domain/plagiarism/PlagiarismVerdict.java b/src/main/java/de/tum/in/www1/artemis/domain/plagiarism/PlagiarismVerdict.java index 83a6064be54e..b8cb8190edd9 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/plagiarism/PlagiarismVerdict.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/plagiarism/PlagiarismVerdict.java @@ -13,7 +13,7 @@ public enum PlagiarismVerdict { /** * Finds the most severe plagiarism verdict where severity is defined by the order of the enum constants in {@link PlagiarismVerdict} * (The first enum constant is the most severe). - * + *

    * In the intended usage scenario, all members of the plagiarismVerdicts should belong to the same student and in the same course or exam. * * @param plagiarismVerdicts an iterable of plagiarism verdicts. diff --git a/src/main/java/de/tum/in/www1/artemis/domain/quiz/QuizPointStatistic.java b/src/main/java/de/tum/in/www1/artemis/domain/quiz/QuizPointStatistic.java index 6f6e5c1ade19..85ea1116d9a9 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/quiz/QuizPointStatistic.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/quiz/QuizPointStatistic.java @@ -119,7 +119,7 @@ public void removeOldResult(Double score, Boolean rated) { */ private void changeStatisticBasedOnResult(double score, Boolean rated, int countChange) { /** - * {@link de.tum.in.www1.artemis.service.util.RoundingUtil#roundScoreSpecifiedByCourseSettings} + * {@link RoundingUtil#roundScoreSpecifiedByCourseSettings} * is not applicable here, as we need to sort the points into existing integer buckets */ double points = Math.round(quiz.getOverallQuizPoints() * (score / 100)); diff --git a/src/main/java/de/tum/in/www1/artemis/domain/quiz/ShortAnswerSubmittedText.java b/src/main/java/de/tum/in/www1/artemis/domain/quiz/ShortAnswerSubmittedText.java index dafe7d38f511..9bd0d8414cb8 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/quiz/ShortAnswerSubmittedText.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/quiz/ShortAnswerSubmittedText.java @@ -78,7 +78,7 @@ public void setSubmittedAnswer(ShortAnswerSubmittedAnswer shortAnswerSubmittedAn } /** - * This function checks if the submittedText (typos included) matches the solution. https://github.com/xdrop/fuzzywuzzy + * This function checks if the submittedText (typos included) matches the solution. ... * * @param submittedText for a short answer question * @param solution of the short answer question diff --git a/src/main/java/de/tum/in/www1/artemis/domain/science/ScienceEventType.java b/src/main/java/de/tum/in/www1/artemis/domain/science/ScienceEventType.java index 7946d5ca71a9..2f2c7733bcae 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/science/ScienceEventType.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/science/ScienceEventType.java @@ -2,7 +2,7 @@ /** * Types of events that can be logged for scientific purposes. - * + *

    * Important: Please follow the naming convention __ */ public enum ScienceEventType { diff --git a/src/main/java/de/tum/in/www1/artemis/domain/tutorialgroups/TutorialGroupSchedule.java b/src/main/java/de/tum/in/www1/artemis/domain/tutorialgroups/TutorialGroupSchedule.java index f68422bc5855..6b3fc12d2f93 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/tutorialgroups/TutorialGroupSchedule.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/tutorialgroups/TutorialGroupSchedule.java @@ -63,7 +63,7 @@ public class TutorialGroupSchedule extends DomainObject { /** * Currently represents weekly recurrence, so 1 means every week, 2 means every other week, etc. *

    - * E.g. if the tutorial group meets every Monday then the {@link #repetitionFrequency} is 1. + * E.g. if the tutorial group meets every Monday then the value is 1. */ @Column(name = "repetition_frequency") private Integer repetitionFrequency; @@ -71,7 +71,7 @@ public class TutorialGroupSchedule extends DomainObject { /** * The date from which this recurrence pattern starts. *

    - * For example, if the tutorial group meets every Monday from 2021-01-01 to 2021-06-30, then the {@link #validFromInclusive} is 2021-01-01. + * For example, if the tutorial group meets every Monday from 2021-01-01 to 2021-06-30, then the value is 2021-01-01. * The first session will be on 2021-01-04 (Monday) and the last session will be on 2021-06-28 (Monday). *

    * The date is in ISO 8601 format. @@ -84,7 +84,7 @@ public class TutorialGroupSchedule extends DomainObject { /** * The date until which this recurrence pattern is valid. *

    - * For example, if the tutorial group meets every Monday from 2021-01-01 to 2021-06-30, then the {@link #validToInclusive} is 2021-06-30. + * For example, if the tutorial group meets every Monday from 2021-01-01 to 2021-06-30, then the value is 2021-06-30. * The first session will be on 2021-01-04 (Monday) and the last session will be on 2021-06-28 (Monday). *

    * The date is in ISO 8601 format. diff --git a/src/main/java/de/tum/in/www1/artemis/domain/view/QuizView.java b/src/main/java/de/tum/in/www1/artemis/domain/view/QuizView.java index 3344e7fd290a..8cbc09feb38b 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/view/QuizView.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/view/QuizView.java @@ -2,7 +2,7 @@ /** * NOTE: This class provides inner classes for use with @JsonView - * + *

    * JsonView can be used to serialize only select properties of an entity to JSON before sending it to the client. Mark attributes in the domain classes * with @JsonView(QuizView.xxx.class) to include that attribute in the xxx view. Attributes without annotations are not serialized by default, if a view is used. This * means, we have to annotate all properties we want to serialize (white-listing) To use it in a REST response, you have to return a MappingJacksonValue object instead of diff --git a/src/main/java/de/tum/in/www1/artemis/repository/ExamRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/ExamRepository.java index a5638f994cf5..fe8f0824aa52 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/ExamRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/ExamRepository.java @@ -416,10 +416,7 @@ default Exam findByIdWithExerciseGroupsElseThrow(long examId) { */ default Set findAllExercisesByExamId(long examId) { var exam = findWithExerciseGroupsAndExercisesById(examId); - if (exam.isEmpty()) { - return Set.of(); - } - return exam.get().getExerciseGroups().stream().map(ExerciseGroup::getExercises).flatMap(Collection::stream).collect(Collectors.toSet()); + return exam.map(value -> value.getExerciseGroups().stream().map(ExerciseGroup::getExercises).flatMap(Collection::stream).collect(Collectors.toSet())).orElseGet(Set::of); } /** diff --git a/src/main/java/de/tum/in/www1/artemis/repository/GradingScaleRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/GradingScaleRepository.java index b7203698ffbf..81d77602c2ce 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/GradingScaleRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/GradingScaleRepository.java @@ -241,6 +241,6 @@ default GradingScale deleteExcessiveGradingScales(long entityId, boolean isExam) for (int i = 1; i < gradingScales.size(); i++) { deleteById(gradingScales.get(i).getId()); } - return gradingScales.get(0); + return gradingScales.getFirst(); } } diff --git a/src/main/java/de/tum/in/www1/artemis/repository/ParticipationRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/ParticipationRepository.java index 5c248c969410..c8c20bd142dd 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/ParticipationRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/ParticipationRepository.java @@ -121,7 +121,7 @@ SELECT MIN(p.individualDueDate) /** * Removes all individual due dates of participations for which the individual due date is before the updated due date of the exercise. - * + *

    * Only considers regular course exercises when the due date actually changed. * * @param exercise for which the participations should be updated. diff --git a/src/main/java/de/tum/in/www1/artemis/repository/ProgrammingExerciseRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/ProgrammingExerciseRepository.java index c6c0ff4c7bb5..48e7d9545242 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/ProgrammingExerciseRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/ProgrammingExerciseRepository.java @@ -118,7 +118,7 @@ default ProgrammingExercise findOneByProjectKeyOrThrow(String projectKey, boolea if (exercises.size() != 1) { throw new EntityNotFoundException("No exercise or multiple exercises found for the given project key: " + projectKey); } - return exercises.get(0); + return exercises.getFirst(); } /** diff --git a/src/main/java/de/tum/in/www1/artemis/repository/StaticCodeAnalysisCategoryRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/StaticCodeAnalysisCategoryRepository.java index b4e3bd8916d6..14749463cdad 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/StaticCodeAnalysisCategoryRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/StaticCodeAnalysisCategoryRepository.java @@ -58,9 +58,9 @@ default List>> categoryPairsWithMapping = new ArrayList<>(); for (var category : categories) { - var defaultCategoryMatch = defaultCategories.stream().filter(defaultCategory -> defaultCategory.getName().equals(category.getName())).findFirst(); + var defaultCategoryMatch = defaultCategories.stream().filter(defaultCategory -> defaultCategory.name().equals(category.getName())).findFirst(); if (defaultCategoryMatch.isPresent()) { - var categoryMappings = defaultCategoryMatch.get().getCategoryMappings(); + var categoryMappings = defaultCategoryMatch.get().categoryMappings(); categoryPairsWithMapping.add(new ImmutablePair<>(category, categoryMappings)); } } diff --git a/src/main/java/de/tum/in/www1/artemis/repository/StudentParticipationRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/StudentParticipationRepository.java index 8f07292f04e8..d4f6cc243fd8 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/StudentParticipationRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/StudentParticipationRepository.java @@ -939,7 +939,7 @@ private List filterParticipationsWithRelevantResults(List< if (!relevantResults.isEmpty()) { // make sure to take the latest result relevantResults.sort((r1, r2) -> r2.getCompletionDate().compareTo(r1.getCompletionDate())); - Result correctResult = relevantResults.get(0); + Result correctResult = relevantResults.getFirst(); relevantResults.clear(); relevantResults.add(correctResult); } diff --git a/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisChatSessionRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisChatSessionRepository.java index a0bbe712f719..e6ca5f2686b3 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisChatSessionRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisChatSessionRepository.java @@ -76,6 +76,6 @@ default IrisChatSession findNewestByExerciseIdAndUserIdElseThrow(long exerciseId if (result.isEmpty()) { throw new EntityNotFoundException("Iris Chat Session"); } - return result.get(0); + return result.getFirst(); } } diff --git a/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisCodeEditorSessionRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisCodeEditorSessionRepository.java index 0cc22151650e..22bbc1c9891a 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisCodeEditorSessionRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisCodeEditorSessionRepository.java @@ -75,6 +75,6 @@ default IrisCodeEditorSession findNewestByExerciseIdAndUserIdElseThrow(long exer if (result.isEmpty()) { throw new EntityNotFoundException("Iris Code Editor Session"); } - return result.get(0); + return result.getFirst(); } } diff --git a/src/main/java/de/tum/in/www1/artemis/repository/specs/MessageSpecs.java b/src/main/java/de/tum/in/www1/artemis/repository/specs/MessageSpecs.java index 2d7b532bbb71..2149d9bf7bdc 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/specs/MessageSpecs.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/specs/MessageSpecs.java @@ -202,7 +202,7 @@ else if (postSortCriterion == PostSortCriterion.VOTES) { * * @return specification that adds the keyword GROUP BY to the query since DISTINCT and ORDER BY keywords are * incompatible with each other at server tests - * https://github.com/h2database/h2database/issues/408 + * ... */ public static Specification distinct() { return (root, query, criteriaBuilder) -> { diff --git a/src/main/java/de/tum/in/www1/artemis/security/jwt/JWTConfigurer.java b/src/main/java/de/tum/in/www1/artemis/security/jwt/JWTConfigurer.java index bee14c5440a7..e5125d1dc57e 100644 --- a/src/main/java/de/tum/in/www1/artemis/security/jwt/JWTConfigurer.java +++ b/src/main/java/de/tum/in/www1/artemis/security/jwt/JWTConfigurer.java @@ -5,17 +5,35 @@ import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +/** + * A custom SecurityConfigurer that integrates JWT authentication into Spring Security's filter chain. + * This configurer is attached to HttpSecurity to apply JWT token verification before processing authentication. + */ public class JWTConfigurer extends SecurityConfigurerAdapter { private final TokenProvider tokenProvider; + /** + * Constructs a JWTConfigurer with a specified token provider. + * + * @param tokenProvider the provider responsible for generating and validating JWT tokens. + */ public JWTConfigurer(TokenProvider tokenProvider) { this.tokenProvider = tokenProvider; } + /** + * Configures the HttpSecurity to add the JWTFilter before the UsernamePasswordAuthenticationFilter. + * This setup ensures that the JWT tokens are extracted and validated from incoming requests + * before any username and password authentication is performed. + * + * @param http the HttpSecurity to configure. + */ @Override public void configure(HttpSecurity http) { JWTFilter customFilter = new JWTFilter(tokenProvider); + // Adds the JWTFilter to the security chain before the UsernamePasswordAuthenticationFilter. + // This ensures that the JWTFilter processes the request first to extract and validate JWTs. http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); } } diff --git a/src/main/java/de/tum/in/www1/artemis/security/lti/Lti13TokenRetriever.java b/src/main/java/de/tum/in/www1/artemis/security/lti/Lti13TokenRetriever.java index e7b205f514ec..d9c98ad993fc 100644 --- a/src/main/java/de/tum/in/www1/artemis/security/lti/Lti13TokenRetriever.java +++ b/src/main/java/de/tum/in/www1/artemis/security/lti/Lti13TokenRetriever.java @@ -96,7 +96,6 @@ public String getToken(ClientRegistration clientRegistration, String... scopes) * that the consuming service may require. * @return A serialized signed JWT as a String. * @throws IllegalArgumentException If no JWK could be retrieved for the provided client registration ID. - * @throws JOSEException If there is an error creating the RSA key pair or signing the JWT. */ public String createDeepLinkingJWT(String clientRegistrationId, Map customClaims) { JWK jwk = oAuth2JWKSService.getJWK(clientRegistrationId); diff --git a/src/main/java/de/tum/in/www1/artemis/service/AssessmentService.java b/src/main/java/de/tum/in/www1/artemis/service/AssessmentService.java index 72643c5f0094..b8b0954ef84f 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/AssessmentService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/AssessmentService.java @@ -17,6 +17,7 @@ import de.tum.in.www1.artemis.service.connectors.lti.LtiNewResultService; import de.tum.in.www1.artemis.service.exam.ExamDateService; import de.tum.in.www1.artemis.service.notifications.SingleUserNotificationService; +import de.tum.in.www1.artemis.service.programming.ProgrammingAssessmentService; import de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException; import de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException; import de.tum.in.www1.artemis.web.websocket.ResultWebsocketService; @@ -41,8 +42,6 @@ public class AssessmentService { protected final SubmissionRepository submissionRepository; - protected final GradingCriterionRepository gradingCriterionRepository; - protected final UserRepository userRepository; private final SubmissionService submissionService; @@ -55,8 +54,8 @@ public class AssessmentService { public AssessmentService(ComplaintResponseService complaintResponseService, ComplaintRepository complaintRepository, FeedbackRepository feedbackRepository, ResultRepository resultRepository, StudentParticipationRepository studentParticipationRepository, ResultService resultService, SubmissionService submissionService, - SubmissionRepository submissionRepository, ExamDateService examDateService, GradingCriterionRepository gradingCriterionRepository, UserRepository userRepository, - Optional ltiNewResultService, SingleUserNotificationService singleUserNotificationService, ResultWebsocketService resultWebsocketService) { + SubmissionRepository submissionRepository, ExamDateService examDateService, UserRepository userRepository, Optional ltiNewResultService, + SingleUserNotificationService singleUserNotificationService, ResultWebsocketService resultWebsocketService) { this.complaintResponseService = complaintResponseService; this.complaintRepository = complaintRepository; this.feedbackRepository = feedbackRepository; @@ -66,7 +65,6 @@ public AssessmentService(ComplaintResponseService complaintResponseService, Comp this.submissionService = submissionService; this.submissionRepository = submissionRepository; this.examDateService = examDateService; - this.gradingCriterionRepository = gradingCriterionRepository; this.userRepository = userRepository; this.ltiNewResultService = ltiNewResultService; this.singleUserNotificationService = singleUserNotificationService; diff --git a/src/main/java/de/tum/in/www1/artemis/service/AuthorizationCheckService.java b/src/main/java/de/tum/in/www1/artemis/service/AuthorizationCheckService.java index 4a1d689636f9..20c800392008 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/AuthorizationCheckService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/AuthorizationCheckService.java @@ -163,7 +163,7 @@ public void isAtLeastEditorInCourseElseThrow(long courseId) { * Given any type of exercise, the method returns if the current user is at least TA for the course the exercise belongs to. If exercise is not present, it will return false, * because the optional will be empty, and therefore `isPresent()` will return false This is due how `filter` works: If a value is present, apply the provided mapping function * to it, and if the result is non-null, return an Optional describing the result. Otherwise, return an empty Optional. - * https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Optional.html#filter(java.util.function.Predicate) + * ... * * @param exercise the exercise that needs to be checked * @param The type of the concrete exercise, because Exercise is an abstract class @@ -303,7 +303,7 @@ private void checkIsAtLeastStudentInCourseElseThrow(@NotNull Course course, @Nul * An enum that represents the different reasons why a user is not allowed to self enroll in a course, * or ALLOWED if the user is allowed to self enroll in the course. */ - private enum EnrollmentAuthorization { + public enum EnrollmentAuthorization { ALLOWED, USERNAME_PATTERN, ENROLLMENT_STATUS, ENROLLMENT_PERIOD, ONLINE, ORGANIZATIONS } @@ -375,7 +375,7 @@ public void checkUserAllowedToEnrollInCourseElseThrow(User user, Course course) * An enum that represents the different reasons why a user is not allowed to unenroll from a course, * or ALLOWED if the user is allowed to unenroll from the course. */ - private enum UnenrollmentAuthorization { + public enum UnenrollmentAuthorization { ALLOWED, UNENROLLMENT_STATUS, UNENROLLMENT_PERIOD, ONLINE } diff --git a/src/main/java/de/tum/in/www1/artemis/service/BuildLogEntryService.java b/src/main/java/de/tum/in/www1/artemis/service/BuildLogEntryService.java index a32d3a00feec..87da92504a88 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/BuildLogEntryService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/BuildLogEntryService.java @@ -201,7 +201,7 @@ public boolean checkIfBuildLogIsNotADuplicate(ProgrammingLanguage programmingLan if (!skipLanguage && !buildLogs.isEmpty()) { // E.g. Swift produces a lot of duplicate build logs when a build fails var existingLog = buildLogs.stream().filter(log -> log.getLog().equals(shortenedLogString)).findFirst(); - String lastLog = buildLogs.get(buildLogs.size() - 1).getLog(); + String lastLog = buildLogs.getLast().getLog(); // If the log does not exist already or if the log is a single blank log add it to the build logs (avoid more than one empty log in a row) boolean isSingleBlankLog = shortenedLogString.isBlank() && !lastLog.isBlank(); return existingLog.isEmpty() || isSingleBlankLog; diff --git a/src/main/java/de/tum/in/www1/artemis/service/ComplaintResponseService.java b/src/main/java/de/tum/in/www1/artemis/service/ComplaintResponseService.java index 2641914336db..bda16d72d3a6 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/ComplaintResponseService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/ComplaintResponseService.java @@ -47,7 +47,7 @@ public ComplaintResponseService(ComplaintRepository complaintRepository, Complai /** * Removes the empty complaint response and thus the lock for a given complaint - * + *

    * The empty complaint response acts as a lock. Only the reviewer of the empty complaint response and instructors can resolve the complaint as long as the lock * is running. For lock duration calculation see: {@link ComplaintResponse#isCurrentlyLocked()}. These methods remove the current empty complaint response thus removing the * lock @@ -87,13 +87,13 @@ private ComplaintResponse getComplaintResponseRepresentingALock(Complaint compla /** * Refreshes the empty complaint response for a given complaint that acts a lock - * + *

    * The empty complaint response acts as a lock. Only the reviewer of the empty complaint response and instructors can resolve the complaint as long as the lock * is running. For lock duration calculation see: {@link ComplaintResponse#isCurrentlyLocked()}. These methods exchange the current empty complaint response to a new one * thus updating the lock. - * + *

    * This is possible in two cases: - * + *

    * Case A: Lock is currently active -> Only the initial reviewer or an instructor can refresh the empty complaint response * Case B: Lock has run out --> Others teaching assistants can refresh the empty complaint response thus acquiring the lock * @@ -129,7 +129,7 @@ public ComplaintResponse refreshComplaintResponseRepresentingLock(Complaint comp /** * Creates an empty complaint response for a given complaint that acts a lock - * + *

    * The empty complaint response acts as a lock. Only the creator of the empty complaint response and instructors can resolve the complaint as long as the lock * is running. For lock duration calculation see: {@link ComplaintResponse#isCurrentlyLocked()} * @@ -161,7 +161,7 @@ public ComplaintResponse createComplaintResponseRepresentingLock(Complaint compl /** * Resolves a complaint by filling in the empty complaint response attached to it - * + *

    * The empty complaint response acts as a lock. Only the creator of the empty complaint response and instructors can resolve empty complaint response as long as the lock * is running. For lock duration calculation see: {@link ComplaintResponse#isCurrentlyLocked()}. These methods fill in the initial complaint response and either accepts * or denies the associated complaint, thus resolving the complaint @@ -248,10 +248,10 @@ public boolean blockedByLock(ComplaintResponse complaintResponseRepresentingLock /** * Checks whether the reviewer is authorized to respond to this complaint, note: instructors are always allowed * to respond to complaints - * + *

    * 1. Team Exercises * => The team tutor assesses the submissions and responds to complaints and more feedback requests - * + *

    * 2. Individual Exercises * => Complaints can only be handled by a tutor who is not the original assessor * => Complaints of exam test runs can be assessed by instructors. They are identified by the same user being the assessor and student diff --git a/src/main/java/de/tum/in/www1/artemis/service/ConsistencyCheckService.java b/src/main/java/de/tum/in/www1/artemis/service/ConsistencyCheckService.java index a306516551cd..d8cbd8702eb0 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/ConsistencyCheckService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/ConsistencyCheckService.java @@ -51,9 +51,9 @@ public List checkConsistencyOfProgrammingExercise(long exer /** * Performs multiple checks for a given programming exercise and returns a list * of the resulting errors, if any. - * + *

    * Make sure to load Template and Solution participations along with the programming exercise. - * + *

    * Checks: * - Existence of VCS repositories and project * - Existence of CI build plans diff --git a/src/main/java/de/tum/in/www1/artemis/service/CourseScoreCalculationService.java b/src/main/java/de/tum/in/www1/artemis/service/CourseScoreCalculationService.java index 9b89610a3316..2a1de47c008c 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/CourseScoreCalculationService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/CourseScoreCalculationService.java @@ -413,7 +413,7 @@ public Result getResultForParticipation(Participation participation, ZonedDateTi } if (ratedResultsWithCompletionDate.size() == 1) { - return ratedResultsWithCompletionDate.get(0); + return ratedResultsWithCompletionDate.getFirst(); } // Sort the list in descending order to have the latest result at the beginning. @@ -421,7 +421,7 @@ public Result getResultForParticipation(Participation participation, ZonedDateTi if (dueDate == null) { // If the due date is null, you can always submit something, and it will always ge graded. Just take the latest graded result. - return resultsList.get(0); + return resultsList.getFirst(); } // The due date is set and we need to find the latest result that was completed before the due date. diff --git a/src/main/java/de/tum/in/www1/artemis/service/ExampleSubmissionService.java b/src/main/java/de/tum/in/www1/artemis/service/ExampleSubmissionService.java index 8d56349cecba..5dfdeb4433be 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/ExampleSubmissionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/ExampleSubmissionService.java @@ -112,9 +112,8 @@ public ExampleSubmission importStudentSubmissionAsExampleSubmission(Long submiss var gradingCriteria = this.gradingCriterionRepository.findByExerciseIdWithEagerGradingCriteria(exercise.getId()); Map gradingInstructionCopyTracker = new HashMap<>(); - gradingCriteria.stream().flatMap(gradingCriterion -> gradingCriterion.getStructuredGradingInstructions().stream()).forEach(gradingInstruction -> { - gradingInstructionCopyTracker.put(gradingInstruction.getId(), gradingInstruction); - }); + gradingCriteria.stream().flatMap(gradingCriterion -> gradingCriterion.getStructuredGradingInstructions().stream()) + .forEach(gradingInstruction -> gradingInstructionCopyTracker.put(gradingInstruction.getId(), gradingInstruction)); if (exercise instanceof ModelingExercise) { ModelingSubmission modelingSubmission = (ModelingSubmission) submissionRepository.findOneWithEagerResultAndFeedback(submissionId); diff --git a/src/main/java/de/tum/in/www1/artemis/service/GradingScaleService.java b/src/main/java/de/tum/in/www1/artemis/service/GradingScaleService.java index 509f3b229fb2..d75d2f3b353c 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/GradingScaleService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/GradingScaleService.java @@ -60,7 +60,7 @@ public GradingScale saveGradingScale(GradingScale gradingScale) { * If the user does not have ADMIN role, they can only access the grading scales if they are an instructor in the course related to it. * The result is paged, meaning that there is only a predefined portion of the result returned to the user, so that the server doesn't * have to send too many results. - * + *

    * The search term is the title of the course or exam that is directly associated with that grading scale. * * @param search The search query defining the search term and the size of the returned page @@ -120,9 +120,9 @@ private boolean gradeStepSetMapsToValidGradingScale(Set gradeSteps) { // check if all pairs of the sorted grade steps have valid adjacency boolean validAdjacency = IntStream.range(0, sortedGradeSteps.size() - 1).allMatch(i -> GradeStep.checkValidAdjacency(sortedGradeSteps.get(i), sortedGradeSteps.get(i + 1))); // first step should start from and include 0 - boolean validFirstElement = sortedGradeSteps.get(0).isLowerBoundInclusive() && sortedGradeSteps.get(0).getLowerBoundPercentage() == 0; + boolean validFirstElement = sortedGradeSteps.getFirst().isLowerBoundInclusive() && sortedGradeSteps.getFirst().getLowerBoundPercentage() == 0; // last step should end with an inclusive value - boolean validLastElement = sortedGradeSteps.get(sortedGradeSteps.size() - 1).isUpperBoundInclusive(); + boolean validLastElement = sortedGradeSteps.getLast().isUpperBoundInclusive(); return validAdjacency && validFirstElement && validLastElement; } diff --git a/src/main/java/de/tum/in/www1/artemis/service/InternalUrlService.java b/src/main/java/de/tum/in/www1/artemis/service/InternalUrlService.java index baca1bcc16fa..e145b7f9aa81 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/InternalUrlService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/InternalUrlService.java @@ -20,9 +20,9 @@ @Service public abstract class InternalUrlService { - protected Optional internalCiUrl; + protected final Optional internalCiUrl; - protected Optional internalVcsUrl; + protected final Optional internalVcsUrl; private static final Logger log = LoggerFactory.getLogger(InternalUrlService.class); @@ -68,7 +68,7 @@ public String toInternalVcsUrl(String vcsRepositoryUri) { if (vcsRepositoryUri == null) { log.warn("Cannot replace url to internal url {} because the url is null.", internalVcsUrl); - return vcsRepositoryUri; + return null; } return replaceUrl(vcsRepositoryUri, internalVcsUrl.get()); diff --git a/src/main/java/de/tum/in/www1/artemis/service/LectureUnitProcessingService.java b/src/main/java/de/tum/in/www1/artemis/service/LectureUnitProcessingService.java index 839951c8a444..bf95ee33c336 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/LectureUnitProcessingService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/LectureUnitProcessingService.java @@ -87,10 +87,10 @@ public List splitAndSaveUnits(LectureUnitInformationDTO lectureU List documentUnits = pdfSplitter.split(document); pdDocumentInformation.setTitle(lectureUnit.unitName()); if (!StringUtils.isEmpty(lectureUnitInformationDTO.removeSlidesCommaSeparatedKeyPhrases())) { - removeSlidesContainingAnyKeyPhrases(documentUnits.get(0), lectureUnitInformationDTO.removeSlidesCommaSeparatedKeyPhrases()); + removeSlidesContainingAnyKeyPhrases(documentUnits.getFirst(), lectureUnitInformationDTO.removeSlidesCommaSeparatedKeyPhrases()); } - documentUnits.get(0).setDocumentInformation(pdDocumentInformation); - documentUnits.get(0).save(outputStream); + documentUnits.getFirst().setDocumentInformation(pdDocumentInformation); + documentUnits.getFirst().save(outputStream); // setup attachmentUnit and attachment attachmentUnit.setDescription(""); @@ -101,8 +101,8 @@ public List splitAndSaveUnits(LectureUnitInformationDTO lectureU MultipartFile multipartFile = fileService.convertByteArrayToMultipart(lectureUnit.unitName(), ".pdf", outputStream.toByteArray()); AttachmentUnit savedAttachmentUnit = attachmentUnitService.createAttachmentUnit(attachmentUnit, attachment, lecture, multipartFile, true); - slideSplitterService.splitAttachmentUnitIntoSingleSlides(documentUnits.get(0), savedAttachmentUnit, multipartFile.getOriginalFilename()); - documentUnits.get(0).close(); // make sure to close the document + slideSplitterService.splitAttachmentUnitIntoSingleSlides(documentUnits.getFirst(), savedAttachmentUnit, multipartFile.getOriginalFilename()); + documentUnits.getFirst().close(); // make sure to close the document units.add(savedAttachmentUnit); } lectureRepository.save(lecture); diff --git a/src/main/java/de/tum/in/www1/artemis/service/LectureUnitService.java b/src/main/java/de/tum/in/www1/artemis/service/LectureUnitService.java index 8ef7704a0aab..326c46fbfa9d 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/LectureUnitService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/LectureUnitService.java @@ -2,7 +2,10 @@ import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE; +import java.net.MalformedURLException; import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; @@ -12,6 +15,7 @@ import java.util.stream.Collectors; import jakarta.validation.constraints.NotNull; +import jakarta.ws.rs.BadRequestException; import org.springframework.context.annotation.Profile; import org.springframework.dao.DataIntegrityViolationException; @@ -197,4 +201,20 @@ public void removeCompetency(Set lectureUnits, Competency competenc lectureUnits.forEach(lectureUnit -> lectureUnit.getCompetencies().remove(competency)); lectureUnitRepository.saveAll(lectureUnits); } + + /** + * Validates the given URL string and returns the URL object + * + * @param urlString The URL string to validate + * @return The URL object + * @throws BadRequestException If the URL string is invalid + */ + public URL validateUrlStringAndReturnUrl(String urlString) { + try { + return new URI(urlString).toURL(); + } + catch (URISyntaxException | MalformedURLException | IllegalArgumentException e) { + throw new BadRequestException(); + } + } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/ModelingExerciseImportService.java b/src/main/java/de/tum/in/www1/artemis/service/ModelingExerciseImportService.java index 1380e743d654..cacc8933086b 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/ModelingExerciseImportService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/ModelingExerciseImportService.java @@ -37,7 +37,7 @@ public ModelingExerciseImportService(ModelingExerciseRepository modelingExercise /** * Imports a modeling exercise creating a new entity, copying all basic values and saving it in the database. * All basic include everything except Student-, Tutor participations, and student questions.
    - * This method calls {@link #copyModelingExerciseBasis(Exercise, Map)} to set up the basis of the exercise + * This method calls {@link #copyModelingExerciseBasis(Exercise, Map)} to set up the basis of the exercise * {@link #copyExampleSubmission(Exercise, Exercise)} for a hard copy of the example submissions. * * @param templateExercise The template exercise which should get imported diff --git a/src/main/java/de/tum/in/www1/artemis/service/ParticipationLifecycleService.java b/src/main/java/de/tum/in/www1/artemis/service/ParticipationLifecycleService.java index 5074d46dd114..746655f5cdd1 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/ParticipationLifecycleService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/ParticipationLifecycleService.java @@ -31,7 +31,7 @@ public ParticipationLifecycleService(@Qualifier("taskScheduler") TaskScheduler s /** * Sets up a scheduled {@link Runnable} task in the lifecycle of a {@link Participation}. - * + *

    * Tasks are performed in a background thread managed by a {@code TaskScheduler}. * See {@code TaskSchedulingConfiguration}. *

    @@ -71,7 +71,7 @@ private Optional getDateForLifecycle(Participation participation, /** * Finds the time when the build and test after due date task should be run. - * + *

    * If the participation has an individual due date, this date may be after the regular build and test date for the exercise. * Therefore, always the latest possible of the two options has to be chosen. * diff --git a/src/main/java/de/tum/in/www1/artemis/service/ParticipationService.java b/src/main/java/de/tum/in/www1/artemis/service/ParticipationService.java index 288e830c1534..3a5390c711b6 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/ParticipationService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/ParticipationService.java @@ -708,7 +708,7 @@ public void cleanupRepository(ProgrammingExerciseStudentParticipation participat /** * Updates the individual due date for each given participation. - * + *

    * Only sets individual due dates if the exercise has a due date and the * individual due date is after this regular due date. * diff --git a/src/main/java/de/tum/in/www1/artemis/service/QuizStatisticService.java b/src/main/java/de/tum/in/www1/artemis/service/QuizStatisticService.java index 825f203c9b5b..852e7090fc9e 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/QuizStatisticService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/QuizStatisticService.java @@ -13,7 +13,6 @@ import org.springframework.stereotype.Service; import de.tum.in.www1.artemis.domain.Result; -import de.tum.in.www1.artemis.domain.participation.Participation; import de.tum.in.www1.artemis.domain.participation.StudentParticipation; import de.tum.in.www1.artemis.domain.quiz.*; import de.tum.in.www1.artemis.repository.*; @@ -76,7 +75,7 @@ public void recalculateStatistics(QuizExercise quizExercise) { } // add the Results in every participation of the given quizExercise to the statistics - for (Participation participation : studentParticipationRepository.findByExerciseId(quizExercise.getId())) { + for (StudentParticipation participation : studentParticipationRepository.findByExerciseId(quizExercise.getId())) { Result latestRatedResult = null; Result latestUnratedResult = null; @@ -104,9 +103,7 @@ public void recalculateStatistics(QuizExercise quizExercise) { quizExercise.addResultToAllStatistics(latestUnratedResult, latestUnratedSubmission); } - if (ltiNewResultService.isPresent()) { - ltiNewResultService.get().onNewResult((StudentParticipation) participation); - } + ltiNewResultService.ifPresent(newResultService -> newResultService.onNewResult(participation)); } // save changed Statistics diff --git a/src/main/java/de/tum/in/www1/artemis/service/ResultService.java b/src/main/java/de/tum/in/www1/artemis/service/ResultService.java index b59cfd4fe0e8..40767f0e4213 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/ResultService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/ResultService.java @@ -330,7 +330,7 @@ else if (planKey.endsWith("-" + BuildPlanType.SOLUTION.getName())) { .findWithResultsAndExerciseAndTeamStudentsByBuildPlanId(planKey); ProgrammingExerciseStudentParticipation participation = null; if (!participations.isEmpty()) { - participation = participations.get(0); + participation = participations.getFirst(); if (participations.size() > 1) { // in the rare case of multiple participations, take the latest one. for (ProgrammingExerciseStudentParticipation otherParticipation : participations) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/StaticCodeAnalysisService.java b/src/main/java/de/tum/in/www1/artemis/service/StaticCodeAnalysisService.java index 953aa6b3b370..4aa2775aee00 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/StaticCodeAnalysisService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/StaticCodeAnalysisService.java @@ -49,10 +49,10 @@ public void createDefaultCategories(ProgrammingExercise programmingExercise) { List newCategories = new ArrayList<>(); for (var defaultCategory : defaultConfiguration) { StaticCodeAnalysisCategory newCategory = new StaticCodeAnalysisCategory(); - newCategory.setName(defaultCategory.getName()); - newCategory.setPenalty(defaultCategory.getPenalty()); - newCategory.setMaxPenalty(defaultCategory.getMaxPenalty()); - newCategory.setState(defaultCategory.getState()); + newCategory.setName(defaultCategory.name()); + newCategory.setPenalty(defaultCategory.penalty()); + newCategory.setMaxPenalty(defaultCategory.maxPenalty()); + newCategory.setState(defaultCategory.state()); newCategory.setProgrammingExercise(programmingExercise); newCategories.add(newCategory); } @@ -110,11 +110,11 @@ public Set resetCategories(ProgrammingExercise exerc // Restore the default configuration. Ignore unknown categories by iterating over the default categories for (var defaultCategory : defaultCategories) { - var matchingCategory = categories.stream().filter(category -> Objects.equals(defaultCategory.getName(), category.getName())).findFirst(); + var matchingCategory = categories.stream().filter(category -> Objects.equals(defaultCategory.name(), category.getName())).findFirst(); matchingCategory.ifPresent(cat -> { - cat.setPenalty(defaultCategory.getPenalty()); - cat.setMaxPenalty(defaultCategory.getMaxPenalty()); - cat.setState(defaultCategory.getState()); + cat.setPenalty(defaultCategory.penalty()); + cat.setMaxPenalty(defaultCategory.maxPenalty()); + cat.setState(defaultCategory.state()); }); } staticCodeAnalysisCategoryRepository.saveAll(categories); diff --git a/src/main/java/de/tum/in/www1/artemis/service/TextAssessmentService.java b/src/main/java/de/tum/in/www1/artemis/service/TextAssessmentService.java index 451283efcb0c..ad6f45ce71c1 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/TextAssessmentService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/TextAssessmentService.java @@ -31,7 +31,7 @@ public TextAssessmentService(UserRepository userRepository, ComplaintResponseSer SubmissionService submissionService, Optional ltiNewResultService, SingleUserNotificationService singleUserNotificationService, ResultWebsocketService resultWebsocketService) { super(complaintResponseService, complaintRepository, feedbackRepository, resultRepository, studentParticipationRepository, resultService, submissionService, - submissionRepository, examDateService, gradingCriterionRepository, userRepository, ltiNewResultService, singleUserNotificationService, resultWebsocketService); + submissionRepository, examDateService, userRepository, ltiNewResultService, singleUserNotificationService, resultWebsocketService); this.textBlockService = textBlockService; } diff --git a/src/main/java/de/tum/in/www1/artemis/service/TutorEffortService.java b/src/main/java/de/tum/in/www1/artemis/service/TutorEffortService.java index 156a356307c3..e82a8b094d64 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/TutorEffortService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/TutorEffortService.java @@ -65,8 +65,8 @@ public List buildTutorEffortList(Long courseId, Long exerciseId) { private TutorEffort createTutorEffortWithInformation(Long userId, List events, int submissions) { TutorEffort effort = new TutorEffort(); effort.setUserId(userId); - effort.setCourseId(events.get(0).getCourseId()); - effort.setExerciseId(events.get(0).getTextExerciseId()); + effort.setCourseId(events.getFirst().getCourseId()); + effort.setExerciseId(events.getFirst().getTextExerciseId()); effort.setTotalTimeSpentMinutes(calculateTutorOverallTimeSpent(events)); effort.setNumberOfSubmissionsAssessed(submissions); return effort; diff --git a/src/main/java/de/tum/in/www1/artemis/service/TutorParticipationService.java b/src/main/java/de/tum/in/www1/artemis/service/TutorParticipationService.java index d4c4bf9ca6a8..7920a3f3a4aa 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/TutorParticipationService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/TutorParticipationService.java @@ -51,16 +51,7 @@ enum FeedbackCorrectionErrorType { /** * Wraps the information of tutor feedback validation (during tutor training). */ - static class FeedbackCorrectionError { - - public String reference; - - public FeedbackCorrectionErrorType type; - - public FeedbackCorrectionError(String reference, FeedbackCorrectionErrorType type) { - this.reference = reference; - this.type = type; - } + record FeedbackCorrectionError(String reference, FeedbackCorrectionErrorType type) { } public TutorParticipationService(TutorParticipationRepository tutorParticipationRepository, ExampleSubmissionRepository exampleSubmissionRepository, @@ -184,7 +175,7 @@ private Optional checkTutorFeedbackForErrors(Feedba throw new IllegalStateException("Multiple instructor feedback exist with the same reference"); } - return tutorFeedbackMatchesInstructorFeedback(tutorFeedback, matchingInstructorFeedback.get(0)); + return tutorFeedbackMatchesInstructorFeedback(tutorFeedback, matchingInstructorFeedback.getFirst()); } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/ZipFileService.java b/src/main/java/de/tum/in/www1/artemis/service/ZipFileService.java index ea8638987429..929fc59849a1 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/ZipFileService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/ZipFileService.java @@ -14,8 +14,8 @@ import jakarta.annotation.Nullable; -import org.apache.commons.compress.utils.FileNameUtils; import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Profile; @@ -94,12 +94,12 @@ public Path createZipFileWithFolderContent(Path zipFilePath, Path contentRootPat * @throws IOException if an error occurred while extracting */ public void extractZipFileRecursively(Path zipPath) throws IOException { - var dirToUnzip = Files.createDirectory(zipPath.toAbsolutePath().getParent().resolve(FileNameUtils.getBaseName(zipPath.toString()))); + var dirToUnzip = Files.createDirectory(zipPath.toAbsolutePath().getParent().resolve(FilenameUtils.getBaseName(zipPath.toString()))); try (ZipFile zipFile = new ZipFile(zipPath.toFile())) { zipFile.extractAll(dirToUnzip.toString()); } List zipFilesInDirList; - try (var zipFilesInDir = Files.list(dirToUnzip).filter(path -> "zip".equalsIgnoreCase(FileNameUtils.getExtension(path.toString())))) { + try (var zipFilesInDir = Files.list(dirToUnzip).filter(path -> "zip".equalsIgnoreCase(FilenameUtils.getExtension(path.toString())))) { zipFilesInDirList = zipFilesInDir.toList(); } for (Path path : zipFilesInDirList) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/compass/umlmodel/Similarity.java b/src/main/java/de/tum/in/www1/artemis/service/compass/umlmodel/Similarity.java index 772002e027fe..3a8016ac86f9 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/compass/umlmodel/Similarity.java +++ b/src/main/java/de/tum/in/www1/artemis/service/compass/umlmodel/Similarity.java @@ -4,12 +4,12 @@ * Similarity is implemented by classes of which an object can calculate the similarity between itself and another similarity object. This interface should be implemented using the * type of the implementing class as T to ensure that the object passed to the similarity methods is always of the same type as the implementing class. In this way, the similarity * is calculated between two objects of the same type which is the intended behavior in most cases. - * + *

    * This interface provides two methods for calculating the similarity between two Similarity objects. If the implementing class contains child entities (e.g. a UML class * containing attributes and methods) that should be separately handled in the similarity calculation, overallSimilarity(Similarity reference) can be overridden to provide the * specific overall similarity calculation including the child entities. Otherwise, it returns the value of the normal similarity(Similarity reference) method. */ -public interface Similarity { +public interface Similarity> { /** * Calculates the similarity between this and another reference object of type Similarity. Takes all attributes of the type into account. diff --git a/src/main/java/de/tum/in/www1/artemis/service/compass/umlmodel/parsers/v2/ComponentDiagramParser.java b/src/main/java/de/tum/in/www1/artemis/service/compass/umlmodel/parsers/v2/ComponentDiagramParser.java index 16973e3129cc..5d53df24c492 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/compass/umlmodel/parsers/v2/ComponentDiagramParser.java +++ b/src/main/java/de/tum/in/www1/artemis/service/compass/umlmodel/parsers/v2/ComponentDiagramParser.java @@ -130,7 +130,6 @@ protected static UMLComponent parseComponent(JsonObject componentJson) { * * @param allUmlElementsMap map of uml elements and ids to find owner element * @param ownerRelationships map of uml elements and ids of their owners - * @return the UMLComponent object parsed from the JSON object */ protected static void resolveParentComponent(Map allUmlElementsMap, Map ownerRelationships) { for (var ownerEntry : ownerRelationships.entrySet()) { @@ -147,7 +146,6 @@ protected static void resolveParentComponent(Map allUmlEleme * @param ownerRelationships map of uml relationship elements and their ids * @param jsonObject json representation of element * @param umlElement uml element - * @return the UMLComponent object parsed from the JSON object */ protected static void findOwner(Map ownerRelationships, JsonObject jsonObject, UMLElement umlElement) { if (jsonObject.has(ELEMENT_OWNER) && !jsonObject.get(ELEMENT_OWNER).isJsonNull()) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/competency/CompetencyProgressService.java b/src/main/java/de/tum/in/www1/artemis/service/competency/CompetencyProgressService.java index 19136c325fcd..9beeee822a7e 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/competency/CompetencyProgressService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/competency/CompetencyProgressService.java @@ -297,7 +297,7 @@ public void deleteProgressForCompetency(long competencyId) { public CourseCompetencyProgressDTO getCompetencyCourseProgress(@NotNull Competency competency, @NotNull Course course) { var numberOfStudents = competencyProgressRepository.countByCompetency(competency.getId()); var numberOfMasteredStudents = competencyProgressRepository.countByCompetencyAndProgressAndConfidenceGreaterThanEqual(competency.getId(), 100.0, - (double) competency.getMasteryThreshold()); + competency.getMasteryThreshold()); var averageStudentScore = RoundingUtil.roundScoreSpecifiedByCourseSettings(competencyProgressRepository.findAverageConfidenceByCompetencyId(competency.getId()).orElse(0.0), course); return new CourseCompetencyProgressDTO(competency.getId(), numberOfStudents, numberOfMasteredStudents, averageStudentScore); diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/GitService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/GitService.java index aeddfe1033a1..30e1c41aad94 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/GitService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/GitService.java @@ -1272,7 +1272,7 @@ public void combineAllCommitsIntoInitialCommit(Repository repo) throws IllegalSt try (Git git = new Git(repo)) { resetToOriginHead(repo); List commits = StreamSupport.stream(git.log().call().spliterator(), false).toList(); - RevCommit firstCommit = commits.get(commits.size() - 1); + RevCommit firstCommit = commits.getLast(); // If there is a first commit, combine all other commits into it. if (firstCommit != null) { git.reset().setMode(ResetCommand.ResetType.SOFT).setRef(firstCommit.getId().getName()).call(); diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/SAML2Service.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/SAML2Service.java index d94ee5d361db..f2a2aa277b8a 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/SAML2Service.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/SAML2Service.java @@ -30,13 +30,13 @@ /** * This class describes a service for SAML2 authentication. - * + *

    * The main method is {@link #handleAuthentication(Saml2AuthenticatedPrincipal)}. The service extracts the user information * from the {@link Saml2AuthenticatedPrincipal} and creates the user, if it does not exist already. - * + *

    * When the user gets created, the SAML2 attributes can be used to fill in user information. The configuration happens * via patterns for every field in the SAML2 configuration. - * + *

    * The service creates a {@link UsernamePasswordAuthenticationToken} which can then be used by the client to authenticate. * This is needed, since the client "does not know" that he is already authenticated via SAML2. */ @@ -86,7 +86,7 @@ private Map generateExtractionPatterns(final SAML2Properties pr /** * Handles an authentication via SAML2. - * + *

    * Registers new users and returns a new {@link UsernamePasswordAuthenticationToken} matching the SAML2 user. * * @param principal the principal, containing the user information diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/athena/AthenaRepositoryExportService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/athena/AthenaRepositoryExportService.java index ed88fa87d67a..0230e0ecdaa4 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/athena/AthenaRepositoryExportService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/athena/AthenaRepositoryExportService.java @@ -104,7 +104,7 @@ public File exportRepository(long exerciseId, Long submissionId, RepositoryType var submission = programmingSubmissionRepository.findById(submissionId).orElseThrow(); // Load participation with eager submissions var participation = (ProgrammingExerciseStudentParticipation) programmingExerciseStudentParticipationRepository - .findWithSubmissionsById(submission.getParticipation().getId()).get(0); + .findWithSubmissionsById(submission.getParticipation().getId()).getFirst(); zipFile = programmingExerciseExportService.createZipForRepositoryWithParticipation(programmingExercise, participation, exportOptions, exportDir, exportDir); } else { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/AbstractContinuousIntegrationResultService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/AbstractContinuousIntegrationResultService.java index b335b3664674..73764cf73c78 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/AbstractContinuousIntegrationResultService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/AbstractContinuousIntegrationResultService.java @@ -9,7 +9,6 @@ import de.tum.in.www1.artemis.domain.participation.Participation; import de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseParticipation; import de.tum.in.www1.artemis.repository.*; -import de.tum.in.www1.artemis.service.BuildLogEntryService; import de.tum.in.www1.artemis.service.dto.AbstractBuildResultNotificationDTO; import de.tum.in.www1.artemis.service.dto.BuildJobDTOInterface; import de.tum.in.www1.artemis.service.hestia.TestwiseCoverageService; @@ -17,27 +16,18 @@ public abstract class AbstractContinuousIntegrationResultService implements ContinuousIntegrationResultService { - protected final ProgrammingSubmissionRepository programmingSubmissionRepository; - - protected final FeedbackRepository feedbackRepository; - protected final ProgrammingExerciseTestCaseRepository testCaseRepository; - protected final BuildLogEntryService buildLogService; - protected final BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository; protected final TestwiseCoverageService testwiseCoverageService; protected final ProgrammingExerciseFeedbackCreationService feedbackCreationService; - protected AbstractContinuousIntegrationResultService(ProgrammingSubmissionRepository programmingSubmissionRepository, FeedbackRepository feedbackRepository, - ProgrammingExerciseTestCaseRepository testCaseRepository, BuildLogEntryService buildLogService, BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, - TestwiseCoverageService testwiseCoverageService, ProgrammingExerciseFeedbackCreationService feedbackCreationService) { - this.programmingSubmissionRepository = programmingSubmissionRepository; - this.feedbackRepository = feedbackRepository; + protected AbstractContinuousIntegrationResultService(ProgrammingExerciseTestCaseRepository testCaseRepository, + BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, TestwiseCoverageService testwiseCoverageService, + ProgrammingExerciseFeedbackCreationService feedbackCreationService) { this.testCaseRepository = testCaseRepository; - this.buildLogService = buildLogService; this.buildLogStatisticsEntryRepository = buildLogStatisticsEntryRepository; this.testwiseCoverageService = testwiseCoverageService; this.feedbackCreationService = feedbackCreationService; diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/AbstractContinuousIntegrationService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/AbstractContinuousIntegrationService.java index e1c9cce3fc90..8e401d6aa780 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/AbstractContinuousIntegrationService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/AbstractContinuousIntegrationService.java @@ -1,82 +1,4 @@ package de.tum.in.www1.artemis.service.connectors.ci; -import java.time.ZonedDateTime; -import java.util.List; -import java.util.function.Predicate; - -import de.tum.in.www1.artemis.domain.BuildLogEntry; -import de.tum.in.www1.artemis.repository.BuildLogStatisticsEntryRepository; -import de.tum.in.www1.artemis.repository.FeedbackRepository; -import de.tum.in.www1.artemis.repository.ProgrammingSubmissionRepository; -import de.tum.in.www1.artemis.service.BuildLogEntryService; -import de.tum.in.www1.artemis.service.hestia.TestwiseCoverageService; - public abstract class AbstractContinuousIntegrationService implements ContinuousIntegrationService { - - protected final ProgrammingSubmissionRepository programmingSubmissionRepository; - - protected final FeedbackRepository feedbackRepository; - - protected final BuildLogEntryService buildLogService; - - protected final BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository; - - protected final TestwiseCoverageService testwiseCoverageService; - - protected AbstractContinuousIntegrationService(ProgrammingSubmissionRepository programmingSubmissionRepository, FeedbackRepository feedbackRepository, - BuildLogEntryService buildLogService, BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, TestwiseCoverageService testwiseCoverageService) { - this.programmingSubmissionRepository = programmingSubmissionRepository; - this.feedbackRepository = feedbackRepository; - this.buildLogService = buildLogService; - this.buildLogStatisticsEntryRepository = buildLogStatisticsEntryRepository; - this.testwiseCoverageService = testwiseCoverageService; - } - - /** - * Find the ZonedDateTime of the first BuildLogEntry that contains the searchString in the log message. - * - * @param buildLogEntries the BuildLogEntries that should be searched - * @param searchString the text that must be contained in the log message - * @return the ZonedDateTime of the found BuildLogEntry, or null if none was found - */ - protected ZonedDateTime getTimestampForLogEntry(List buildLogEntries, String searchString) { - return getTimestampForLogEntry(buildLogEntries, searchString, 0); - } - - /** - * Find the ZonedDateTime of the nth BuildLogEntry that contains the searchString in the log message. - * This method does not return the first entry that matches the searchString but skips skipEntries matching BuildLogEntries. - * - * @param buildLogEntries the BuildLogEntries that should be searched - * @param searchString the text that must be contained in the log message - * @param skipEntries the number of matching BuildLogEntries that should be skipped - * @return the ZonedDateTime of the found BuildLogEntry, or null if none was found - */ - protected ZonedDateTime getTimestampForLogEntry(List buildLogEntries, String searchString, int skipEntries) { - return getTimestampForLogEntry(buildLogEntries, buildLogEntry -> buildLogEntry.getLog().contains(searchString), skipEntries); - } - - /** - * Find the ZonedDateTime of the first BuildLogEntry that matches the given predicate. - * - * @param buildLogEntries the BuildLogEntries that should be searched - * @param matchingPredicate the predicate that must be true for the BuildLogEntry - * @return the ZonedDateTime of the found BuildLogEntry, or null if none was found - */ - protected ZonedDateTime getTimestampForLogEntry(List buildLogEntries, Predicate matchingPredicate) { - return getTimestampForLogEntry(buildLogEntries, matchingPredicate, 0); - } - - /** - * Find the ZonedDateTime of the nth BuildLogEntry that matches the given predicate. - * This method does not return the first entry that matches the given predicate but skips skipEntries matching BuildLogEntries. - * - * @param buildLogEntries the BuildLogEntries that should be searched - * @param matchingPredicate the predicate that must be true for the BuildLogEntry - * @param skipEntries the number of matching BuildLogEntries that should be skipped - * @return the ZonedDateTime of the found BuildLogEntry, or null if none was found - */ - protected ZonedDateTime getTimestampForLogEntry(List buildLogEntries, Predicate matchingPredicate, int skipEntries) { - return buildLogEntries.stream().filter(matchingPredicate).skip(skipEntries).findFirst().map(BuildLogEntry::getTime).orElse(null); - } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/notification/BuildLogParseUtils.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/notification/BuildLogParseUtils.java index 909b42438657..b47bab4a3db7 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/notification/BuildLogParseUtils.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/notification/BuildLogParseUtils.java @@ -14,11 +14,11 @@ public class BuildLogParseUtils { /** * Parses build logs from Jenkins or GitLab CI into BuildLogEntry objects. The function reads the list * of log strings and tries to parse lines of the following format: - * + *

    * [2021-05-10T15:19:49.741Z] [INFO] BUILD FAILURE - * + *

    * and extract the timestamp and message. - * + *

    * A small snippet of the log format is: * [Pipeline] { * [Pipeline] sh diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/notification/dto/TestCaseDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/notification/dto/TestCaseDTO.java index e877084f28c9..b177d78b651a 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/notification/dto/TestCaseDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/notification/dto/TestCaseDTO.java @@ -43,20 +43,20 @@ private Optional extractMessage() { boolean hasSuccessInfos = !ObjectUtils.isEmpty(successInfos()); boolean successful = isSuccessful(); - if (successful && hasSuccessInfos && successInfos().get(0).getMostInformativeMessage() != null) { - return Optional.of(successInfos().get(0).getMostInformativeMessage()); + if (successful && hasSuccessInfos && successInfos().getFirst().getMostInformativeMessage() != null) { + return Optional.of(successInfos().getFirst().getMostInformativeMessage()); } - else if (hasErrors && errors().get(0).getMostInformativeMessage() != null) { - return Optional.of(errors().get(0).getMostInformativeMessage()); + else if (hasErrors && errors().getFirst().getMostInformativeMessage() != null) { + return Optional.of(errors().getFirst().getMostInformativeMessage()); } - else if (hasFailures && failures().get(0).getMostInformativeMessage() != null) { - return Optional.of(failures().get(0).getMostInformativeMessage()); + else if (hasFailures && failures().getFirst().getMostInformativeMessage() != null) { + return Optional.of(failures().getFirst().getMostInformativeMessage()); } - else if (hasErrors && errors().get(0).type() != null) { - return Optional.of(String.format("Unsuccessful due to an error of type: %s", errors().get(0).type())); + else if (hasErrors && errors().getFirst().type() != null) { + return Optional.of(String.format("Unsuccessful due to an error of type: %s", errors().getFirst().type())); } - else if (hasFailures && failures().get(0).type() != null) { - return Optional.of(String.format("Unsuccessful due to an error of type: %s", failures().get(0).type())); + else if (hasFailures && failures().getFirst().type() != null) { + return Optional.of(String.format("Unsuccessful due to an error of type: %s", failures().getFirst().type())); } else if (!successful) { // this is an edge case which typically does not happen diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabPersonalAccessTokenManagementService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabPersonalAccessTokenManagementService.java index a8927b7933e9..24b6a18f03e6 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabPersonalAccessTokenManagementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabPersonalAccessTokenManagementService.java @@ -180,7 +180,7 @@ private GitLabPersonalAccessTokenListResponseDTO fetchPersonalAccessTokenId(Long } // We assume that there exists no other personal access token with a name that contains the value of PERSONAL_ACCESS_TOKEN_NAME. - return responseBody.get(0); + return responseBody.getFirst(); } private org.gitlab4j.api.models.User getGitLabUserFromUser(User user) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabService.java index 47d5dad7dc6a..40d2361155a9 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabService.java @@ -8,7 +8,6 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.*; -import java.util.Optional; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserManagementService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserManagementService.java index a979062e67e5..c89f93950f2a 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserManagementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserManagementService.java @@ -490,7 +490,7 @@ public org.gitlab4j.api.models.User createUser(User user, String password) { * It is needed if * 1. the config option is enabled, and * 2. the user does not yet have an access token - * + *

    * The GitLab user will be extracted from the Gitlab user API * * @param user the Artemis user (where the token will be stored) diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIResultService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIResultService.java index 9c3388ee61bd..88ca926dd95e 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIResultService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIResultService.java @@ -30,8 +30,7 @@ public class GitLabCIResultService extends AbstractContinuousIntegrationResultSe public GitLabCIResultService(ProgrammingSubmissionRepository programmingSubmissionRepository, FeedbackRepository feedbackRepository, BuildLogEntryService buildLogService, BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, TestwiseCoverageService testwiseCoverageService, ProgrammingExerciseFeedbackCreationService feedbackCreationService, ProgrammingExerciseTestCaseRepository testCaseRepository) { - super(programmingSubmissionRepository, feedbackRepository, testCaseRepository, buildLogService, buildLogStatisticsEntryRepository, testwiseCoverageService, - feedbackCreationService); + super(testCaseRepository, buildLogStatisticsEntryRepository, testwiseCoverageService, feedbackCreationService); } @Override @@ -58,7 +57,7 @@ public void extractAndPersistBuildLogStatistics(ProgrammingSubmission programmin return; } ZonedDateTime jobStarted = getTimestampForLogEntry(buildLogEntries, ""); // First entry; - ZonedDateTime jobFinished = buildLogEntries.get(buildLogEntries.size() - 1).getTime(); // Last entry + ZonedDateTime jobFinished = buildLogEntries.getLast().getTime(); // Last entry ZonedDateTime testsStarted = getTimestampForLogEntry(buildLogEntries, "Scanning for projects..."); ZonedDateTime testsFinished = getTimestampForLogEntry(buildLogEntries, "Total time:"); Integer dependenciesDownloadedCount = countMatchingLogs(buildLogEntries, "Downloaded from"); diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIService.java index 9a24a800a339..63f290f74311 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIService.java @@ -28,13 +28,11 @@ import de.tum.in.www1.artemis.exception.ContinuousIntegrationException; import de.tum.in.www1.artemis.exception.GitLabCIException; import de.tum.in.www1.artemis.repository.*; -import de.tum.in.www1.artemis.service.BuildLogEntryService; import de.tum.in.www1.artemis.service.UriService; import de.tum.in.www1.artemis.service.connectors.ConnectorHealth; import de.tum.in.www1.artemis.service.connectors.ci.AbstractContinuousIntegrationService; import de.tum.in.www1.artemis.service.connectors.ci.CIPermission; import de.tum.in.www1.artemis.service.connectors.ci.notification.dto.TestResultsDTO; -import de.tum.in.www1.artemis.service.hestia.TestwiseCoverageService; @Profile("gitlabci") @Service @@ -98,11 +96,8 @@ public class GitLabCIService extends AbstractContinuousIntegrationService { @Value("${artemis.version-control.token}") private String gitlabToken; - public GitLabCIService(ProgrammingSubmissionRepository programmingSubmissionRepository, FeedbackRepository feedbackRepository, BuildLogEntryService buildLogService, - GitLabApi gitlab, UriService uriService, BuildPlanRepository buildPlanRepository, GitLabCIBuildPlanService buildPlanService, - ProgrammingLanguageConfiguration programmingLanguageConfiguration, BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, - TestwiseCoverageService testwiseCoverageService) { - super(programmingSubmissionRepository, feedbackRepository, buildLogService, buildLogStatisticsEntryRepository, testwiseCoverageService); + public GitLabCIService(GitLabApi gitlab, UriService uriService, BuildPlanRepository buildPlanRepository, GitLabCIBuildPlanService buildPlanService, + ProgrammingLanguageConfiguration programmingLanguageConfiguration) { this.gitlab = gitlab; this.uriService = uriService; this.buildPlanRepository = buildPlanRepository; diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsAuthorizationInterceptor.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsAuthorizationInterceptor.java index 01f12fdecb7d..1a7f3839e4f9 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsAuthorizationInterceptor.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsAuthorizationInterceptor.java @@ -60,7 +60,7 @@ private void setCrumb(final HttpHeaders headersToAuthenticate) { try { final var response = restTemplate.exchange(jenkinsURL.toString() + "/crumbIssuer/api/json", HttpMethod.GET, entity, JsonNode.class); - final var sessionId = response.getHeaders().get("Set-Cookie").get(0); + final var sessionId = response.getHeaders().get("Set-Cookie").getFirst(); headersToAuthenticate.add("Jenkins-Crumb", response.getBody().get("crumb").asText()); headersToAuthenticate.add("Cookie", sessionId); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsResultService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsResultService.java index c4982d307ca9..ba63a5f01105 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsResultService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsResultService.java @@ -31,8 +31,7 @@ public class JenkinsResultService extends AbstractContinuousIntegrationResultSer public JenkinsResultService(ProgrammingSubmissionRepository programmingSubmissionRepository, FeedbackRepository feedbackRepository, BuildLogEntryService buildLogService, BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, TestwiseCoverageService testwiseCoverageService, ProgrammingExerciseFeedbackCreationService feedbackCreationService, ProgrammingExerciseTestCaseRepository testCaseRepository) { - super(programmingSubmissionRepository, feedbackRepository, testCaseRepository, buildLogService, buildLogStatisticsEntryRepository, testwiseCoverageService, - feedbackCreationService); + super(testCaseRepository, buildLogStatisticsEntryRepository, testwiseCoverageService, feedbackCreationService); } @Override @@ -59,7 +58,7 @@ public void extractAndPersistBuildLogStatistics(ProgrammingSubmission programmin ZonedDateTime testsFinished; ZonedDateTime scaStarted; ZonedDateTime scaFinished; - ZonedDateTime jobFinished = buildLogEntries.get(buildLogEntries.size() - 1).getTime(); // Last entry + ZonedDateTime jobFinished = buildLogEntries.getLast().getTime(); // Last entry Integer dependenciesDownloadedCount = null; if (ProjectType.isMavenProject(projectType)) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsService.java index ee2dbb9116fb..9273f8f9815b 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsService.java @@ -26,11 +26,7 @@ import de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseParticipation; import de.tum.in.www1.artemis.exception.ContinuousIntegrationException; import de.tum.in.www1.artemis.exception.JenkinsException; -import de.tum.in.www1.artemis.repository.BuildLogStatisticsEntryRepository; -import de.tum.in.www1.artemis.repository.FeedbackRepository; import de.tum.in.www1.artemis.repository.ProgrammingExerciseRepository; -import de.tum.in.www1.artemis.repository.ProgrammingSubmissionRepository; -import de.tum.in.www1.artemis.service.BuildLogEntryService; import de.tum.in.www1.artemis.service.ProfileService; import de.tum.in.www1.artemis.service.connectors.ConnectorHealth; import de.tum.in.www1.artemis.service.connectors.aeolus.AeolusTemplateService; @@ -40,7 +36,6 @@ import de.tum.in.www1.artemis.service.connectors.ci.notification.dto.TestResultsDTO; import de.tum.in.www1.artemis.service.connectors.jenkins.build_plan.JenkinsBuildPlanService; import de.tum.in.www1.artemis.service.connectors.jenkins.jobs.JenkinsJobService; -import de.tum.in.www1.artemis.service.hestia.TestwiseCoverageService; @Profile("jenkins") @Service @@ -70,12 +65,9 @@ public class JenkinsService extends AbstractContinuousIntegrationService { private final ProgrammingExerciseRepository programmingExerciseRepository; - public JenkinsService(JenkinsServer jenkinsServer, ProgrammingSubmissionRepository programmingSubmissionRepository, FeedbackRepository feedbackRepository, - @Qualifier("shortTimeoutJenkinsRestTemplate") RestTemplate shortTimeoutRestTemplate, BuildLogEntryService buildLogService, - BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, JenkinsBuildPlanService jenkinsBuildPlanService, JenkinsJobService jenkinsJobService, - JenkinsInternalUrlService jenkinsInternalUrlService, TestwiseCoverageService testwiseCoverageService, Optional aeolusTemplateService, - ProfileService profileService, ProgrammingExerciseRepository programmingExerciseRepository) { - super(programmingSubmissionRepository, feedbackRepository, buildLogService, buildLogStatisticsEntryRepository, testwiseCoverageService); + public JenkinsService(JenkinsServer jenkinsServer, @Qualifier("shortTimeoutJenkinsRestTemplate") RestTemplate shortTimeoutRestTemplate, + JenkinsBuildPlanService jenkinsBuildPlanService, JenkinsJobService jenkinsJobService, JenkinsInternalUrlService jenkinsInternalUrlService, + Optional aeolusTemplateService, ProfileService profileService, ProgrammingExerciseRepository programmingExerciseRepository) { this.jenkinsServer = jenkinsServer; this.jenkinsBuildPlanService = jenkinsBuildPlanService; this.jenkinsJobService = jenkinsJobService; diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsUserManagementService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsUserManagementService.java index 663e14e4ed38..7d205d82cfa1 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsUserManagementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsUserManagementService.java @@ -209,7 +209,8 @@ public void updateUserLogin(String oldLogin, User user, String password) throws * @param password the user's password * @param groupsToAdd groups to add the user to * @param groupsToRemove groups to remove the user from - * @throws ContinuousIntegrationException + * + * @throws ContinuousIntegrationException if something went wrong updating the user */ @Override public void updateUserAndGroups(String oldLogin, User user, String password, Set groupsToAdd, Set groupsToRemove) throws ContinuousIntegrationException { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIResultService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIResultService.java index b8b5068d334a..33b1030efcff 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIResultService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIResultService.java @@ -11,7 +11,6 @@ import de.tum.in.www1.artemis.domain.enumeration.ProjectType; import de.tum.in.www1.artemis.exception.LocalCIException; import de.tum.in.www1.artemis.repository.*; -import de.tum.in.www1.artemis.service.BuildLogEntryService; import de.tum.in.www1.artemis.service.connectors.ci.AbstractContinuousIntegrationResultService; import de.tum.in.www1.artemis.service.connectors.localci.dto.LocalCIBuildResult; import de.tum.in.www1.artemis.service.dto.AbstractBuildResultNotificationDTO; @@ -25,11 +24,9 @@ @Profile("localci") public class LocalCIResultService extends AbstractContinuousIntegrationResultService { - public LocalCIResultService(ProgrammingSubmissionRepository programmingSubmissionRepository, FeedbackRepository feedbackRepository, BuildLogEntryService buildLogService, - TestwiseCoverageService testwiseCoverageService, BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, + public LocalCIResultService(TestwiseCoverageService testwiseCoverageService, BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, ProgrammingExerciseFeedbackCreationService feedbackCreationService, ProgrammingExerciseTestCaseRepository testCaseRepository) { - super(programmingSubmissionRepository, feedbackRepository, testCaseRepository, buildLogService, buildLogStatisticsEntryRepository, testwiseCoverageService, - feedbackCreationService); + super(testCaseRepository, buildLogStatisticsEntryRepository, testwiseCoverageService, feedbackCreationService); } @Override diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIService.java index cd16e71765ab..0554ff12e7d4 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIService.java @@ -16,18 +16,13 @@ import de.tum.in.www1.artemis.domain.VcsRepositoryUri; import de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseParticipation; import de.tum.in.www1.artemis.exception.LocalCIException; -import de.tum.in.www1.artemis.repository.BuildLogStatisticsEntryRepository; -import de.tum.in.www1.artemis.repository.FeedbackRepository; import de.tum.in.www1.artemis.repository.ProgrammingExerciseRepository; -import de.tum.in.www1.artemis.repository.ProgrammingSubmissionRepository; -import de.tum.in.www1.artemis.service.BuildLogEntryService; import de.tum.in.www1.artemis.service.connectors.BuildScriptProviderService; import de.tum.in.www1.artemis.service.connectors.ConnectorHealth; import de.tum.in.www1.artemis.service.connectors.aeolus.AeolusTemplateService; import de.tum.in.www1.artemis.service.connectors.aeolus.Windfile; import de.tum.in.www1.artemis.service.connectors.ci.AbstractContinuousIntegrationService; import de.tum.in.www1.artemis.service.connectors.ci.CIPermission; -import de.tum.in.www1.artemis.service.hestia.TestwiseCoverageService; /** * Implementation of ContinuousIntegrationService for local CI. Contains methods for communication with the local CI system. @@ -46,10 +41,8 @@ public class LocalCIService extends AbstractContinuousIntegrationService { private final ProgrammingExerciseRepository programmingExerciseRepository; - public LocalCIService(ProgrammingSubmissionRepository programmingSubmissionRepository, FeedbackRepository feedbackRepository, BuildLogEntryService buildLogService, - BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, TestwiseCoverageService testwiseCoverageService, - BuildScriptProviderService buildScriptProviderService, AeolusTemplateService aeolusTemplateService, ProgrammingExerciseRepository programmingExerciseRepository) { - super(programmingSubmissionRepository, feedbackRepository, buildLogService, buildLogStatisticsEntryRepository, testwiseCoverageService); + public LocalCIService(BuildScriptProviderService buildScriptProviderService, AeolusTemplateService aeolusTemplateService, + ProgrammingExerciseRepository programmingExerciseRepository) { this.buildScriptProviderService = buildScriptProviderService; this.aeolusTemplateService = aeolusTemplateService; this.programmingExerciseRepository = programmingExerciseRepository; diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/LocalCIDockerService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/LocalCIDockerService.java index 7cf447501ddd..ffd41c2fde62 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/LocalCIDockerService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/LocalCIDockerService.java @@ -4,7 +4,6 @@ import java.time.Instant; import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.locks.ReentrantLock; @@ -188,7 +187,7 @@ public void deleteOldDockerImages() { // Delete images that have not been used for more than imageExpiryDays days for (String dockerImage : dockerImageCleanupInfo.keySet()) { if (imageNames.contains(dockerImage)) { - if (dockerImageCleanupInfo.get(dockerImage).isBefore(ZonedDateTime.now().minus(imageExpiryDays, ChronoUnit.DAYS))) { + if (dockerImageCleanupInfo.get(dockerImage).isBefore(ZonedDateTime.now().minusDays(imageExpiryDays))) { log.info("Deleting docker image {}", dockerImage); try { dockerClient.removeImageCmd(dockerImage).exec(); diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/SharedQueueProcessingService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/SharedQueueProcessingService.java index c8a3ed5c4899..082810c7e5ac 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/SharedQueueProcessingService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/SharedQueueProcessingService.java @@ -216,7 +216,7 @@ private LocalCIBuildAgentInformation getUpdatedLocalBuildAgentInformation(LocalC // TODO: Make this number configurable if (recentBuildJob != null) { if (recentBuildJobs.size() >= 20) { - recentBuildJobs.remove(0); + recentBuildJobs.removeFirst(); } recentBuildJobs.add(recentBuildJob); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/lti/Lti13Service.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/lti/Lti13Service.java index e66927baed9f..c9c7434943c8 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/lti/Lti13Service.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/lti/Lti13Service.java @@ -1,7 +1,7 @@ package de.tum.in.www1.artemis.service.connectors.lti; -import java.net.MalformedURLException; -import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import java.util.Collection; import java.util.Map; import java.util.Optional; @@ -267,9 +267,9 @@ private Optional getExerciseFromTargetLink(String targetLinkUrl) { String targetLinkPath; try { - targetLinkPath = (new URL(targetLinkUrl)).getPath(); + targetLinkPath = new URI(targetLinkUrl).getPath(); } - catch (MalformedURLException ex) { + catch (URISyntaxException ex) { log.info("Malformed target link url: {}", targetLinkUrl); return Optional.empty(); } @@ -308,9 +308,7 @@ private void createOrUpdateResourceLaunch(Lti13LaunchRequest launchRequest, User launch.setUser(user); Optional ltiPlatformConfiguration = ltiPlatformConfigurationRepository.findByRegistrationId(launchRequest.getClientRegistrationId()); - if (ltiPlatformConfiguration.isPresent()) { - launch.setLtiPlatformConfiguration(ltiPlatformConfiguration.get()); - } + ltiPlatformConfiguration.ifPresent(launch::setLtiPlatformConfiguration); launchRepository.save(launch); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/exam/ExamQuizService.java b/src/main/java/de/tum/in/www1/artemis/service/exam/ExamQuizService.java index 5f4dc090110f..b13bb1d0c6f6 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/exam/ExamQuizService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/exam/ExamQuizService.java @@ -153,7 +153,7 @@ public void evaluateQuizParticipationsForTestRunAndTestExam(StudentExam studentE * 2. mark submission and participation as evaluated * 3. Create a new result for the selected submission and calculate scores * 4. Save the updated submission & participation and the newly created result - * + *

    * After processing all participations, the created results will be returned for further processing * Note: We ignore test run participations * // @formatter:on @@ -185,7 +185,7 @@ else if (submissions.size() > 1) { // Load submission with highest id submissionsList.sort(Comparator.comparing(Submission::getId).reversed()); - quizSubmission = (QuizSubmission) submissionsList.get(0); + quizSubmission = (QuizSubmission) submissionsList.getFirst(); } else { quizSubmission = (QuizSubmission) submissions.iterator().next(); diff --git a/src/main/java/de/tum/in/www1/artemis/service/exam/ExamSubmissionService.java b/src/main/java/de/tum/in/www1/artemis/service/exam/ExamSubmissionService.java index 9a1eff75cb91..443e2d35e233 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/exam/ExamSubmissionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/exam/ExamSubmissionService.java @@ -155,7 +155,7 @@ public Submission preventMultipleSubmissions(Exercise exercise, Submission submi List participations = participationService.findByExerciseAndStudentIdWithEagerSubmissions(exercise, user.getId()); if (!participations.isEmpty()) { - Set submissions = participations.get(0).getSubmissions(); + Set submissions = participations.getFirst().getSubmissions(); if (!submissions.isEmpty()) { Submission existingSubmission = submissions.iterator().next(); // Instead of creating a new submission, we want to overwrite the already existing submission. Therefore diff --git a/src/main/java/de/tum/in/www1/artemis/service/exam/ExamUserService.java b/src/main/java/de/tum/in/www1/artemis/service/exam/ExamUserService.java index ac58c0a5a17d..62098b4d1ede 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/exam/ExamUserService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/exam/ExamUserService.java @@ -154,6 +154,6 @@ public void deleteAvailableExamUserImages(ExamUser user) { /** * Contains the information about an exam user with image */ - record ExamUserWithImageDTO(String studentRegistrationNumber, ImageDTO image) { + public record ExamUserWithImageDTO(String studentRegistrationNumber, ImageDTO image) { } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/exam/ImageExtractor.java b/src/main/java/de/tum/in/www1/artemis/service/exam/ImageExtractor.java index f7f9f210a2d0..e3d9133ba005 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/exam/ImageExtractor.java +++ b/src/main/java/de/tum/in/www1/artemis/service/exam/ImageExtractor.java @@ -85,7 +85,7 @@ public ImageExtractor process() { protected void processOperator(Operator operator, List operands) throws IOException { String operation = operator.getName(); if (INVOKE_OPERATOR.equals(operation)) { - COSName objectName = (COSName) operands.get(0); + COSName objectName = (COSName) operands.getFirst(); PDXObject pdxObject = getResources().getXObject(objectName); if (pdxObject instanceof PDImageXObject image) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/exam/StudentExamService.java b/src/main/java/de/tum/in/www1/artemis/service/exam/StudentExamService.java index b79ad3214e51..9d27fed05254 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/exam/StudentExamService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/exam/StudentExamService.java @@ -827,7 +827,7 @@ private StudentExam generateIndividualStudentExam(Exam exam, User student) { // StudentExams are saved in the called method HashSet userHashSet = new HashSet<>(); userHashSet.add(student); - return studentExamRepository.createRandomStudentExams(exam, userHashSet, examQuizQuestionsGenerator).get(0); + return studentExamRepository.createRandomStudentExams(exam, userHashSet, examQuizQuestionsGenerator).getFirst(); } /** diff --git a/src/main/java/de/tum/in/www1/artemis/service/export/CourseExamExportService.java b/src/main/java/de/tum/in/www1/artemis/service/export/CourseExamExportService.java index ccac74e8c89c..05dec893bdd5 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/export/CourseExamExportService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/export/CourseExamExportService.java @@ -419,7 +419,6 @@ else if (exercise instanceof QuizExercise quizExercise) { } else { // Exercise is not supported so skip - continue; } } catch (Exception e) { @@ -492,7 +491,7 @@ private void notifyUserAboutExerciseExportState(String topic, CourseExamExportSt */ private Path writeReport(List data, Path outputDir) throws IOException { List lines = data.stream().map(ArchivalReportEntry::toString).collect(Collectors.toCollection(ArrayList::new)); - lines.add(0, ArchivalReportEntry.getHeadline()); + lines.addFirst(ArchivalReportEntry.getHeadline()); return writeFile(lines, outputDir, "report.csv"); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/export/DataExportService.java b/src/main/java/de/tum/in/www1/artemis/service/export/DataExportService.java index 155261754a60..26088f07bd7b 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/export/DataExportService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/export/DataExportService.java @@ -125,7 +125,7 @@ public DataExportDTO canDownloadAnyDataExport() { return new DataExportDTO(dataExport.getId(), dataExport.getDataExportState(), dataExport.getCreatedDate().atZone(ZoneId.systemDefault()), nextRequestDate); } } - var latestDataExport = dataExportsFromUser.get(0); + var latestDataExport = dataExportsFromUser.getFirst(); return new DataExportDTO(null, latestDataExport.getDataExportState(), latestDataExport.getCreatedDate().atZone(ZoneId.systemDefault()), retrieveNextRequestDate(latestDataExport)); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/export/ProgrammingExerciseExportService.java b/src/main/java/de/tum/in/www1/artemis/service/export/ProgrammingExerciseExportService.java index 4ecbc7bc0206..e886f9f46cdf 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/export/ProgrammingExerciseExportService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/export/ProgrammingExerciseExportService.java @@ -260,10 +260,8 @@ public List exportProgrammingExerciseRepositories(ProgrammingExercise exer List auxiliaryRepositories = auxiliaryRepositoryRepository.findByExerciseId(exercise.getId()); // Export the auxiliary repositories and add them to list - auxiliaryRepositories.forEach(auxiliaryRepository -> { - pathsToBeZipped - .add(exportInstructorAuxiliaryRepositoryForExercise(exercise.getId(), auxiliaryRepository, workingDir, outputDir, exportErrors).map(File::toPath).orElse(null)); - }); + auxiliaryRepositories.forEach(auxiliaryRepository -> pathsToBeZipped + .add(exportInstructorAuxiliaryRepositoryForExercise(exercise.getId(), auxiliaryRepository, workingDir, outputDir, exportErrors).map(File::toPath).orElse(null))); // Setup path to store the zip file for the exported repositories var timestamp = ZonedDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd-Hmss")); diff --git a/src/main/java/de/tum/in/www1/artemis/service/export/SubmissionExportService.java b/src/main/java/de/tum/in/www1/artemis/service/export/SubmissionExportService.java index f4272ec4866b..3c6ba1bf7ec1 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/export/SubmissionExportService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/export/SubmissionExportService.java @@ -69,7 +69,7 @@ public File exportStudentSubmissionsElseThrow(Long exerciseId, SubmissionExportO if (zippedSubmissionsPaths.isEmpty()) { throw new BadRequestAlertException("Failed to export student submissions.", "SubmissionExport", "noSubmissions"); } - return zippedSubmissionsPaths.get(0).toFile(); + return zippedSubmissionsPaths.getFirst().toFile(); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/hestia/ExerciseHintService.java b/src/main/java/de/tum/in/www1/artemis/service/hestia/ExerciseHintService.java index ba63a751ce36..0c85a48d48db 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/hestia/ExerciseHintService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/hestia/ExerciseHintService.java @@ -151,7 +151,7 @@ public Set getAvailableExerciseHints(ProgrammingExercise exercise, return new HashSet<>(); } - var latestResult = submissions.get(0).getLatestResult(); + var latestResult = submissions.getFirst().getLatestResult(); // latest submissions has no result or latest result has no feedback (most commonly due to a build error) if (latestResult == null || latestResult.getFeedbacks().isEmpty()) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/hestia/ProgrammingExerciseGitDiffReportService.java b/src/main/java/de/tum/in/www1/artemis/service/hestia/ProgrammingExerciseGitDiffReportService.java index 3c5a99008da1..bd3551fa3b1d 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/hestia/ProgrammingExerciseGitDiffReportService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/hestia/ProgrammingExerciseGitDiffReportService.java @@ -81,7 +81,7 @@ public ProgrammingExerciseGitDiffReportService(GitService gitService, Programmin * If there were no changes since the last report was created this will not do anything. * If there were changes to at least one of the repositories a new report will be created. * This method should not be called twice for the same programming exercise at the same time, as this will result in - * the creation of 2 reports. See https://github.com/ls1intum/Artemis/pull/4893 for more information about it. + * the creation of 2 reports. See Artemis 4893 for more information about it. * * @param programmingExercise The programming exercise * @return The git-diff report for the given programming exercise @@ -142,7 +142,7 @@ private ProgrammingExerciseGitDiffReport getReportOfExercise(ProgrammingExercise return null; } else if (reports.size() == 1) { - return reports.get(0); + return reports.getFirst(); } // Error handling in case more than one reports exist for the exercise var latestReport = reports.stream().max(Comparator.comparing(DomainObject::getId)).get(); diff --git a/src/main/java/de/tum/in/www1/artemis/service/hestia/TestwiseCoverageService.java b/src/main/java/de/tum/in/www1/artemis/service/hestia/TestwiseCoverageService.java index e528c47279bb..e7c6eb72544c 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/hestia/TestwiseCoverageService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/hestia/TestwiseCoverageService.java @@ -294,7 +294,7 @@ public Optional getCoverageReportForLatestSolutionSubmissionFrom if (reports.isEmpty()) { return Optional.empty(); } - return Optional.of(reports.get(0)); + return Optional.of(reports.getFirst()); } /** @@ -309,6 +309,6 @@ public Optional getFullCoverageReportForLatestSolutionSubmission if (reports.isEmpty()) { return Optional.empty(); } - return Optional.of(reports.get(0)); + return Optional.of(reports.getFirst()); } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/hestia/behavioral/knowledgesource/BehavioralKnowledgeSource.java b/src/main/java/de/tum/in/www1/artemis/service/hestia/behavioral/knowledgesource/BehavioralKnowledgeSource.java index 79a8168907f7..5625fa92281c 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/hestia/behavioral/knowledgesource/BehavioralKnowledgeSource.java +++ b/src/main/java/de/tum/in/www1/artemis/service/hestia/behavioral/knowledgesource/BehavioralKnowledgeSource.java @@ -5,7 +5,7 @@ public abstract class BehavioralKnowledgeSource { - protected BehavioralBlackboard blackboard; + protected final BehavioralBlackboard blackboard; public BehavioralKnowledgeSource(BehavioralBlackboard blackboard) { this.blackboard = blackboard; diff --git a/src/main/java/de/tum/in/www1/artemis/service/hestia/behavioral/knowledgesource/CombineChangeBlocks.java b/src/main/java/de/tum/in/www1/artemis/service/hestia/behavioral/knowledgesource/CombineChangeBlocks.java index afeab1d485c8..ff88833876e3 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/hestia/behavioral/knowledgesource/CombineChangeBlocks.java +++ b/src/main/java/de/tum/in/www1/artemis/service/hestia/behavioral/knowledgesource/CombineChangeBlocks.java @@ -4,7 +4,6 @@ import java.util.TreeSet; import de.tum.in.www1.artemis.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.in.www1.artemis.service.hestia.behavioral.BehavioralSolutionEntryGenerationException; import de.tum.in.www1.artemis.service.hestia.behavioral.GroupedFile; /** @@ -25,7 +24,7 @@ public boolean executeCondition() { } @Override - public boolean executeAction() throws BehavioralSolutionEntryGenerationException { + public boolean executeAction() { boolean didChanges = false; for (GroupedFile groupedFile : blackboard.getGroupedFiles()) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/hestia/behavioral/knowledgesource/DropRemovedGitDiffEntries.java b/src/main/java/de/tum/in/www1/artemis/service/hestia/behavioral/knowledgesource/DropRemovedGitDiffEntries.java index f03150814647..6993b694862e 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/hestia/behavioral/knowledgesource/DropRemovedGitDiffEntries.java +++ b/src/main/java/de/tum/in/www1/artemis/service/hestia/behavioral/knowledgesource/DropRemovedGitDiffEntries.java @@ -4,7 +4,6 @@ import java.util.stream.Collectors; import de.tum.in.www1.artemis.service.hestia.behavioral.BehavioralBlackboard; -import de.tum.in.www1.artemis.service.hestia.behavioral.BehavioralSolutionEntryGenerationException; /** * Remove all {@link de.tum.in.www1.artemis.domain.hestia.ProgrammingExerciseGitDiffEntry} from the @@ -25,7 +24,7 @@ public boolean executeCondition() { } @Override - public boolean executeAction() throws BehavioralSolutionEntryGenerationException { + public boolean executeAction() { var nonRemovedEntries = blackboard.getGitDiffReport().getEntries().stream().filter(entry -> entry.getStartLine() != null && entry.getLineCount() != null) .collect(Collectors.toCollection(HashSet::new)); blackboard.getGitDiffReport().setEntries(nonRemovedEntries); diff --git a/src/main/java/de/tum/in/www1/artemis/service/hestia/structural/StructuralClassElements.java b/src/main/java/de/tum/in/www1/artemis/service/hestia/structural/StructuralClassElements.java index 1421d8cad951..ee571a128468 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/hestia/structural/StructuralClassElements.java +++ b/src/main/java/de/tum/in/www1/artemis/service/hestia/structural/StructuralClassElements.java @@ -11,7 +11,7 @@ * Used for the generation of solution entries for structural test cases */ @JsonIgnoreProperties(ignoreUnknown = true) -class StructuralClassElements { +public class StructuralClassElements { @JsonProperty(value = "class", required = true) private StructuralClass structuralClass; diff --git a/src/main/java/de/tum/in/www1/artemis/service/hestia/structural/StructuralMethod.java b/src/main/java/de/tum/in/www1/artemis/service/hestia/structural/StructuralMethod.java index a920993cc6b6..a9fa7f975bdb 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/hestia/structural/StructuralMethod.java +++ b/src/main/java/de/tum/in/www1/artemis/service/hestia/structural/StructuralMethod.java @@ -72,7 +72,7 @@ private String formatModifiers(StructuralClassElements structuralClassElements, modifiers.remove("abstract"); } else { - modifiers.add(0, "default"); + modifiers.addFirst("default"); } } if (!modifiers.isEmpty()) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/IrisMessageService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/IrisMessageService.java index cae925741728..32a1a93ca8a9 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/IrisMessageService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/IrisMessageService.java @@ -56,6 +56,6 @@ public IrisMessage saveMessage(IrisMessage message, IrisSession session, IrisMes session.setMessages(sessionWithMessages.getMessages()); // Make sure we keep the session up to date as we overrode it. We do this to avoid unnecessarily fetching the // session again. - return sessionWithMessages.getMessages().get(sessionWithMessages.getMessages().size() - 1); + return sessionWithMessages.getMessages().getLast(); } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/IrisRateLimitService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/IrisRateLimitService.java index 33339ad7696b..bbaa8ca8f66f 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/IrisRateLimitService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/IrisRateLimitService.java @@ -37,7 +37,7 @@ public IrisRateLimitService(IrisMessageRepository irisMessageRepository, IrisSet public IrisRateLimitInformation getRateLimitInformation(User user) { var globalSettings = irisSettingsService.getGlobalSettings(); var irisChatSettings = globalSettings.getIrisChatSettings(); - var rateLimitTimeframeHours = Objects.requireNonNullElse(irisChatSettings.getRateLimitTimeframeHours(), 0); + int rateLimitTimeframeHours = Objects.requireNonNullElse(irisChatSettings.getRateLimitTimeframeHours(), 0); var start = ZonedDateTime.now().minusHours(rateLimitTimeframeHours); var end = ZonedDateTime.now(); var currentMessageCount = irisMessageRepository.countLlmResponsesOfUserWithinTimeframe(user.getId(), start, end); diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/exception/IrisException.java b/src/main/java/de/tum/in/www1/artemis/service/iris/exception/IrisException.java index 4aca58b81234..136f96aea364 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/exception/IrisException.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/exception/IrisException.java @@ -10,9 +10,9 @@ public class IrisException extends HttpStatusException { - protected String translationKey; + protected final String translationKey; - protected Map translationParams; + protected final Map translationParams; public IrisException(String translationKey, Map translationParams) { super(ErrorConstants.DEFAULT_TYPE, "An error within Iris has occured", Status.INTERNAL_SERVER_ERROR, "Iris", translationKey, diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisChatSessionService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisChatSessionService.java index 436e3d14c763..1710d7f3980d 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisChatSessionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisChatSessionService.java @@ -216,7 +216,7 @@ private Optional getLatestSubmissionIfExists(ProgrammingE if (participations.isEmpty()) { return Optional.empty(); } - return participations.get(participations.size() - 1).getSubmissions().stream().max(Submission::compareTo) + return participations.getLast().getSubmissions().stream().max(Submission::compareTo) .flatMap(sub -> programmingSubmissionRepository.findWithEagerBuildLogEntriesById(sub.getId())); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCompetencyGenerationSessionService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCompetencyGenerationSessionService.java index 00f8afc6bee2..364a4183f744 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCompetencyGenerationSessionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCompetencyGenerationSessionService.java @@ -109,7 +109,7 @@ record CompetencyGenerationDTO( @Override public List executeRequest(IrisCompetencyGenerationSession session) { - var userMessageContent = irisMessageRepository.findFirstWithContentBySessionIdAndSenderOrderBySentAtDesc(session.getId(), IrisMessageSender.USER).getContent().get(0); + var userMessageContent = irisMessageRepository.findFirstWithContentBySessionIdAndSenderOrderBySentAtDesc(session.getId(), IrisMessageSender.USER).getContent().getFirst(); if (!(userMessageContent instanceof IrisTextMessageContent) || userMessageContent.getContentAsString() == null) { throw new InternalServerErrorException("Unable to get last user message!"); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisHestiaSessionService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisHestiaSessionService.java index f04f03b20a70..5865357153a0 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisHestiaSessionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisHestiaSessionService.java @@ -60,9 +60,9 @@ public IrisHestiaSessionService(IrisConnectorService irisConnectorService, IrisS public IrisHestiaSession getOrCreateSession(CodeHint codeHint) { var existingSessions = irisHestiaSessionRepository.findByCodeHintIdOrderByCreationDateDesc(codeHint.getId()); // Return the newest session if there is one and it is not older than 1 hour - if (!existingSessions.isEmpty() && existingSessions.get(0).getCreationDate().plusHours(1).isAfter(ZonedDateTime.now())) { - checkHasAccessTo(null, existingSessions.get(0)); - return existingSessions.get(0); + if (!existingSessions.isEmpty() && existingSessions.getFirst().getCreationDate().plusHours(1).isAfter(ZonedDateTime.now())) { + checkHasAccessTo(null, existingSessions.getFirst()); + return existingSessions.getFirst(); } // Otherwise create a new session diff --git a/src/main/java/de/tum/in/www1/artemis/service/learningpath/LearningPathNgxService.java b/src/main/java/de/tum/in/www1/artemis/service/learningpath/LearningPathNgxService.java index 65cafafa70c8..6fa94126f2c0 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/learningpath/LearningPathNgxService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/learningpath/LearningPathNgxService.java @@ -276,10 +276,10 @@ private void generateNgxPathRepresentationForCompetency(LearningPath learningPat } else { // add edge from competency start to first learning object - addEdgeFromCompetencyStartToLearningObject(competency, recommendedLearningObjects.get(0), edges); + addEdgeFromCompetencyStartToLearningObject(competency, recommendedLearningObjects.getFirst(), edges); // add edge from last learning object to competency end - addEdgeFromLearningObjectToCompetencyEnd(competency, recommendedLearningObjects.get(recommendedLearningObjects.size() - 1), edges); + addEdgeFromLearningObjectToCompetencyEnd(competency, recommendedLearningObjects.getLast(), edges); } nodes.addAll(currentCluster); diff --git a/src/main/java/de/tum/in/www1/artemis/service/linkpreview/ogparser/OgParser.java b/src/main/java/de/tum/in/www1/artemis/service/linkpreview/ogparser/OgParser.java index eda283412623..231ad693fb6a 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/linkpreview/ogparser/OgParser.java +++ b/src/main/java/de/tum/in/www1/artemis/service/linkpreview/ogparser/OgParser.java @@ -64,7 +64,7 @@ private void updateContents(OgMetaElement ogMetaElement, List contents, } private void setExtraDataOnLastContent(OgMetaElement ogMetaElement, List contents, String contentValue) { - final Content lastContent = contents.get(contents.size() - 1); + final Content lastContent = contents.getLast(); lastContent.setExtraData(ogMetaElement.getExtraData(), contentValue); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/messaging/MainInstanceMessageSendService.java b/src/main/java/de/tum/in/www1/artemis/service/messaging/MainInstanceMessageSendService.java index 9b10f478f4c2..311d8089d5bb 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/messaging/MainInstanceMessageSendService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/messaging/MainInstanceMessageSendService.java @@ -12,7 +12,7 @@ @Profile("scheduling") public class MainInstanceMessageSendService implements InstanceMessageSendService { - public InstanceMessageReceiveService instanceMessageReceiveService; + public final InstanceMessageReceiveService instanceMessageReceiveService; public MainInstanceMessageSendService(InstanceMessageReceiveService instanceMessageReceiveService) { this.instanceMessageReceiveService = instanceMessageReceiveService; diff --git a/src/main/java/de/tum/in/www1/artemis/service/metis/conversation/ConversationDTOService.java b/src/main/java/de/tum/in/www1/artemis/service/metis/conversation/ConversationDTOService.java index 213880555a7e..dc9d852a05cb 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/metis/conversation/ConversationDTOService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/metis/conversation/ConversationDTOService.java @@ -250,7 +250,7 @@ private GroupChatDTO convertGroupChatToDto(User requestingUser, GroupChat groupC private Set getConversationParticipants(Conversation conversation) { Set conversationParticipants; var participantsInitialized = Persistence.getPersistenceUtil().isLoaded(conversation, "conversationParticipants") && conversation.getConversationParticipants() != null - && conversation.getConversationParticipants().size() > 0; + && !conversation.getConversationParticipants().isEmpty(); if (participantsInitialized) { conversationParticipants = conversation.getConversationParticipants(); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/metis/conversation/auth/ChannelAuthorizationService.java b/src/main/java/de/tum/in/www1/artemis/service/metis/conversation/auth/ChannelAuthorizationService.java index 214d7b75c72d..148b087e5cd3 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/metis/conversation/auth/ChannelAuthorizationService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/metis/conversation/auth/ChannelAuthorizationService.java @@ -208,7 +208,7 @@ public boolean hasChannelModerationRights(@NotNull Channel channel, @NotNull Use public void isAllowedToRegisterUsersToChannel(@NotNull Channel channel, @Nullable List userLogins, @NotNull User user) { var userLoginsToCheck = Objects.requireNonNullElse(userLogins, new ArrayList<>()); var userToCheck = getUserIfNecessary(user); - var isJoinRequest = userLoginsToCheck.size() == 1 && userLoginsToCheck.get(0).equals(userToCheck.getLogin()); + var isJoinRequest = userLoginsToCheck.size() == 1 && userLoginsToCheck.getFirst().equals(userToCheck.getLogin()); var channelFromDb = channelRepository.findById(channel.getId()); var isAtLeastInstructor = authorizationCheckService.isAtLeastInstructorInCourse(channelFromDb.orElseThrow().getCourse(), userToCheck); var isChannelModerator = isChannelModerator(channel.getId(), userToCheck.getId()); @@ -269,7 +269,7 @@ public void isAllowedToDeregisterUsersFromChannel(@NotNull Channel channel, @Nul if (!isChannelMember) { throw new AccessForbiddenException("User is not a member of the channel"); } - var isSelfDeRegistration = userLoginsToCheck.size() == 1 && userLoginsToCheck.get(0).equals(userToCheck.getLogin()); + var isSelfDeRegistration = userLoginsToCheck.size() == 1 && userLoginsToCheck.getFirst().equals(userToCheck.getLogin()); if (!isSelfDeRegistration) { throw new AccessForbiddenException("You are not allowed to deregister other users from this channel"); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/notifications/TutorialGroupNotificationService.java b/src/main/java/de/tum/in/www1/artemis/service/notifications/TutorialGroupNotificationService.java index 3cbc70eb6c5e..770516d3bff4 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/notifications/TutorialGroupNotificationService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/notifications/TutorialGroupNotificationService.java @@ -83,9 +83,8 @@ private void sendInstantNotification(TutorialGroupNotification notification, boo private void sendNotificationViaWebSocket(TutorialGroupNotification notification) { // as we send to a general topic, we filter client side by individual notification settings - notification.getTutorialGroup().getRegistrations().stream().map(TutorialGroupRegistration::getStudent).forEach(user -> { - websocketMessagingService.sendMessage(notification.getTopic(user.getId()), notification); - }); + notification.getTutorialGroup().getRegistrations().stream().map(TutorialGroupRegistration::getStudent) + .forEach(user -> websocketMessagingService.sendMessage(notification.getTopic(user.getId()), notification)); websocketMessagingService.sendMessage(notification.getTopic(notification.getTutorialGroup().getTeachingAssistant().getId()), notification); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/notifications/push_notifications/PushNotificationService.java b/src/main/java/de/tum/in/www1/artemis/service/notifications/push_notifications/PushNotificationService.java index 1048403e5ce5..26bbd5fdd1a7 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/notifications/push_notifications/PushNotificationService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/notifications/push_notifications/PushNotificationService.java @@ -79,7 +79,7 @@ void sendNotificationRequestsToEndpoint(List requests, } /** - * Sends the actual request to the Hermes Relay Service (see here: https://github.com/ls1intum/Hermes) + * Sends the actual request to the Hermes Relay Service (see here: ...) * It uses exponential backoff to retry once the request fails * * @param body to be sent to Hermes. Differs between iOS and Android diff --git a/src/main/java/de/tum/in/www1/artemis/service/plagiarism/ProgrammingPlagiarismDetectionService.java b/src/main/java/de/tum/in/www1/artemis/service/plagiarism/ProgrammingPlagiarismDetectionService.java index 532a03fff22e..16b674bb748d 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/plagiarism/ProgrammingPlagiarismDetectionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/plagiarism/ProgrammingPlagiarismDetectionService.java @@ -113,7 +113,7 @@ public ProgrammingPlagiarismDetectionService(FileService fileService, Programmin * @throws ExitException is thrown if JPlag exits unexpectedly * @throws IOException is thrown for file handling errors */ - public TextPlagiarismResult checkPlagiarism(long programmingExerciseId, float similarityThreshold, int minimumScore, int minimumSize) throws ExitException, IOException { + public TextPlagiarismResult checkPlagiarism(long programmingExerciseId, float similarityThreshold, int minimumScore, int minimumSize) throws IOException { long start = System.nanoTime(); String topic = plagiarismWebsocketService.getProgrammingExercisePlagiarismCheckTopic(programmingExerciseId); diff --git a/src/main/java/de/tum/in/www1/artemis/service/plagiarism/TextPlagiarismDetectionService.java b/src/main/java/de/tum/in/www1/artemis/service/plagiarism/TextPlagiarismDetectionService.java index afd5902432f4..8dc8cf991cd8 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/plagiarism/TextPlagiarismDetectionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/plagiarism/TextPlagiarismDetectionService.java @@ -86,7 +86,7 @@ public List textSubmissionsForComparison(TextExercise exerciseWi * @return a zip file that can be returned to the client * @throws ExitException is thrown if JPlag exits unexpectedly */ - public TextPlagiarismResult checkPlagiarism(TextExercise textExercise, float similarityThreshold, int minimumScore, int minimumSize) throws ExitException { + public TextPlagiarismResult checkPlagiarism(TextExercise textExercise, float similarityThreshold, int minimumScore, int minimumSize) { // Only one plagiarism check per course allowed var courseId = textExercise.getCourseViaExerciseGroupOrCourseMember().getId(); diff --git a/src/main/java/de/tum/in/www1/artemis/service/programming/JavaTemplateUpgradeService.java b/src/main/java/de/tum/in/www1/artemis/service/programming/JavaTemplateUpgradeService.java index 707f2241f579..aa4e580a9912 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/programming/JavaTemplateUpgradeService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/programming/JavaTemplateUpgradeService.java @@ -102,8 +102,8 @@ private void upgradeTemplateFiles(ProgrammingExercise exercise, RepositoryType r // Validate that template and repository have the same number of pom.xml files, otherwise no upgrade will take place if (templatePoms.length == 1 && repositoryPoms.size() == 1) { - Model updatedRepoModel = upgradeProjectObjectModel(templatePoms[0], repositoryPoms.get(0), Boolean.TRUE.equals(exercise.isStaticCodeAnalysisEnabled())); - writeProjectObjectModel(updatedRepoModel, repositoryPoms.get(0)); + Model updatedRepoModel = upgradeProjectObjectModel(templatePoms[0], repositoryPoms.getFirst(), Boolean.TRUE.equals(exercise.isStaticCodeAnalysisEnabled())); + writeProjectObjectModel(updatedRepoModel, repositoryPoms.getFirst()); } if (repositoryType == RepositoryType.TESTS) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingAssessmentService.java b/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingAssessmentService.java index 0c6b14a8622b..a3705a7e102f 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingAssessmentService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingAssessmentService.java @@ -35,7 +35,7 @@ public ProgrammingAssessmentService(ComplaintResponseService complaintResponseSe Optional ltiNewResultService, SingleUserNotificationService singleUserNotificationService, ResultWebsocketService resultWebsocketService, ProgrammingExerciseParticipationService programmingExerciseParticipationService, Optional athenaFeedbackSendingService) { super(complaintResponseService, complaintRepository, feedbackRepository, resultRepository, studentParticipationRepository, resultService, submissionService, - submissionRepository, examDateService, gradingCriterionRepository, userRepository, ltiNewResultService, singleUserNotificationService, resultWebsocketService); + submissionRepository, examDateService, userRepository, ltiNewResultService, singleUserNotificationService, resultWebsocketService); this.programmingExerciseParticipationService = programmingExerciseParticipationService; this.athenaFeedbackSendingService = athenaFeedbackSendingService; } diff --git a/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportFromFileService.java b/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportFromFileService.java index 22889e9d7753..9f915ab302c7 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportFromFileService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportFromFileService.java @@ -12,8 +12,8 @@ import java.util.Map; import java.util.stream.Stream; -import org.apache.commons.compress.utils.FileNameUtils; import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.filefilter.NameFileFilter; import org.apache.commons.io.filefilter.NotFileFilter; import org.eclipse.jgit.api.errors.GitAPIException; @@ -92,7 +92,7 @@ public ProgrammingExerciseImportFromFileService(ProgrammingExerciseService progr **/ public ProgrammingExercise importProgrammingExerciseFromFile(ProgrammingExercise originalProgrammingExercise, MultipartFile zipFile, Course course, User user) throws IOException, GitAPIException, URISyntaxException { - if (!"zip".equals(FileNameUtils.getExtension(zipFile.getOriginalFilename()))) { + if (!"zip".equals(FilenameUtils.getExtension(zipFile.getOriginalFilename()))) { throw new BadRequestAlertException("The file is not a zip file", "programmingExercise", "fileNotZip"); } Path importExerciseDir = null; @@ -111,7 +111,7 @@ public ProgrammingExercise importProgrammingExerciseFromFile(ProgrammingExercise if (Boolean.TRUE.equals(originalProgrammingExercise.isStaticCodeAnalysisEnabled())) { staticCodeAnalysisService.createDefaultCategories(newProgrammingExercise); } - Path pathToDirectoryWithImportedContent = exerciseFilePath.toAbsolutePath().getParent().resolve(FileNameUtils.getBaseName(exerciseFilePath.toString())); + Path pathToDirectoryWithImportedContent = exerciseFilePath.toAbsolutePath().getParent().resolve(FilenameUtils.getBaseName(exerciseFilePath.toString())); copyEmbeddedFiles(pathToDirectoryWithImportedContent); importRepositoriesFromFile(newProgrammingExercise, importExerciseDir, oldShortName, user); newProgrammingExercise.setCourse(course); @@ -259,7 +259,7 @@ private Path retrieveRepositoryDirectoryPath(Path dirPath, String repoType) { "There are either no or more than one sub-directories containing " + repoType + " in their name. Please make sure that there is exactly one."); } - return result.get(0); + return result.getFirst(); } private Path retrieveExerciseJsonPath(Path dirPath) throws IOException { @@ -275,6 +275,6 @@ private Path retrieveExerciseJsonPath(Path dirPath) throws IOException { if (result.size() != 1) { throw new BadRequestAlertException("There are either no JSON files or more than one JSON file in the directory!", "programmingExercise", "exerciseJsonNotValidOrFound"); } - return result.get(0); + return result.getFirst(); } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/scheduled/ParticipantScoreScheduleService.java b/src/main/java/de/tum/in/www1/artemis/service/scheduled/ParticipantScoreScheduleService.java index 9107c55d465b..b64c1f6eb6dd 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/scheduled/ParticipantScoreScheduleService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/scheduled/ParticipantScoreScheduleService.java @@ -294,15 +294,13 @@ private void executeTask(Long exerciseId, Long participantId, Instant resultLast teamScore.setExercise(exercise); return teamScore; } - else if (participant instanceof User user) { + else { + User user = (User) participant; var studentScore = new StudentScore(); studentScore.setUser(user); studentScore.setExercise(exercise); return studentScore; } - else { - return null; - } }); // Now do the heavy lifting and calculate the latest score based on all results for this exercise diff --git a/src/main/java/de/tum/in/www1/artemis/service/scheduled/cache/quiz/QuizScheduleService.java b/src/main/java/de/tum/in/www1/artemis/service/scheduled/cache/quiz/QuizScheduleService.java index 79014b2f956e..0057104c7f8d 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/scheduled/cache/quiz/QuizScheduleService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/scheduled/cache/quiz/QuizScheduleService.java @@ -49,7 +49,6 @@ import de.tum.in.www1.artemis.service.QuizStatisticService; import de.tum.in.www1.artemis.service.WebsocketMessagingService; import de.tum.in.www1.artemis.service.connectors.lti.LtiNewResultService; -import de.tum.in.www1.artemis.service.scheduled.cache.Cache; @Profile(PROFILE_CORE) @Service @@ -437,10 +436,9 @@ public void processCachedQuizSubmissions() { log.debug("Process cached quiz submissions"); // global try-catch for error logging try { - for (Cache cache : quizCache.getAllCaches()) { - QuizExerciseCache cachedQuiz = (QuizExerciseCache) cache; + for (QuizExerciseCache cache : quizCache.getAllCaches()) { // this way near cache is used (values will deserialize new objects) - Long quizExerciseId = cachedQuiz.getExerciseId(); + Long quizExerciseId = cache.getExerciseId(); // Get fresh QuizExercise from DB QuizExercise quizExercise = quizExerciseRepository.findOne(quizExerciseId); // check if quiz has been deleted @@ -456,9 +454,9 @@ public void processCachedQuizSubmissions() { // ensure that attempts that were never submitted get committed to the database and saved // this is required to ensure that students cannot gain extra attempts this way - for (var batch : cachedQuiz.getBatches().entrySet()) { + for (var batch : cache.getBatches().entrySet()) { if (batchCache.get(batch.getValue()).isEnded()) { - cachedQuiz.getSubmissions().putIfAbsent(batch.getKey(), new QuizSubmission()); + cache.getSubmissions().putIfAbsent(batch.getKey(), new QuizSubmission()); } } @@ -466,15 +464,15 @@ public void processCachedQuizSubmissions() { boolean hasEnded = quizExercise.isQuizEnded(); // Note that those might not be true later on due to concurrency and a distributed system, // do not rely on that for actions upon the whole set, such as clear() - boolean hasNewSubmissions = !cachedQuiz.getSubmissions().isEmpty(); - boolean hasNewParticipations = !cachedQuiz.getParticipations().isEmpty(); - boolean hasNewResults = !cachedQuiz.getResults().isEmpty(); + boolean hasNewSubmissions = !cache.getSubmissions().isEmpty(); + boolean hasNewParticipations = !cache.getParticipations().isEmpty(); + boolean hasNewResults = !cache.getResults().isEmpty(); // Skip quizzes with no cached changes if (!hasNewSubmissions && !hasNewParticipations && !hasNewResults) { // Remove quiz if it has ended if (hasEnded) { - removeCachedQuiz(cachedQuiz); + removeCachedQuiz(cache); } continue; } @@ -486,8 +484,8 @@ public void processCachedQuizSubmissions() { if (hasNewSubmissions) { // Create Participations and Results if the submission was submitted or if the quiz has ended and save them to Database (DB Write) - Map submissions = cachedQuiz.getSubmissions(); - Map batches = cachedQuiz.getBatches(); + Map submissions = cache.getSubmissions(); + Map batches = cache.getBatches(); // This call will remove the processed Submission map entries itself int numberOfSubmittedSubmissions = saveQuizSubmissionWithParticipationAndResultToDatabase(quizExercise, submissions, batches, batchCache); // .. and likely generate new participations and results @@ -505,7 +503,7 @@ public void processCachedQuizSubmissions() { if (hasNewParticipations && hasEnded) { // Send the participation with containing result and quiz back to the users via websocket and remove the participation from the ParticipationHashMap - Collection> finishedParticipations = cachedQuiz.getParticipations().entrySet(); + Collection> finishedParticipations = cache.getParticipations().entrySet(); // TODO maybe find a better way to optimize the performance (use an executor service with e.g. X parallel threads) finishedParticipations.parallelStream().forEach(entry -> { StudentParticipation participation = entry.getValue(); @@ -518,7 +516,7 @@ public void processCachedQuizSubmissions() { } ltiNewResultService.ifPresent(newResultService->newResultService.onNewResult(participation)); sendQuizResultToUser(quizExerciseId, participation); - cachedQuiz.getParticipations().remove(entry.getKey()); + cache.getParticipations().remove(entry.getKey()); } }); if (!finishedParticipations.isEmpty()) { @@ -533,13 +531,13 @@ public void processCachedQuizSubmissions() { // Fetch a new quiz exercise here including deeper attribute paths (this is relatively expensive, so we only do that if necessary) try { // Get a Set because QuizStatisticService needs one (currently) - Set newResultsForQuiz = Set.copyOf(cachedQuiz.getResults().values()); + Set newResultsForQuiz = Set.copyOf(cache.getResults().values()); // Update the statistics quizStatisticService.updateStatistics(newResultsForQuiz, quizExercise); log.info("Updated statistics with {} new results in {} for quiz {}", newResultsForQuiz.size(), formatDurationFrom(start), quizExercise.getTitle()); // Remove only processed results for (Result result : newResultsForQuiz) { - cachedQuiz.getResults().remove(result.getId()); + cache.getResults().remove(result.getId()); } } catch (Exception e) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/team/strategies/CreateOnlyStrategy.java b/src/main/java/de/tum/in/www1/artemis/service/team/strategies/CreateOnlyStrategy.java index 0340bfff6240..d1c874c91d3c 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/team/strategies/CreateOnlyStrategy.java +++ b/src/main/java/de/tum/in/www1/artemis/service/team/strategies/CreateOnlyStrategy.java @@ -34,7 +34,7 @@ public void importTeams(Exercise exercise, List teams) { /** * Filters the teams from the given source exercise and returns only those that can be imported into the destination exercise without conflicts - * + *

    * Conditions for being conflict-free: * 1. No clash in team short name * 2. No overlapping students @@ -53,7 +53,7 @@ private List getExerciseTeamsAndFindConflictFreeSourceTeams(Exercise sourc /** * Filters the teams from the given source exercise and returns only those that can be imported into the destination exercise without conflicts - * + *

    * Conditions for being conflict-free: * 1. No clash in team short name * 2. No overlapping students @@ -71,7 +71,7 @@ private List getExerciseTeamsAndFindConflictFreeSourceTeams(Exercise exerc /** * Filters the teams from the given team list and returns only those that do not have conflicts with the existing ones - * + *

    * Conditions for being conflict-free: * 1. No clash in team short name * 2. No overlapping students diff --git a/src/main/java/de/tum/in/www1/artemis/service/team/strategies/PurgeExistingStrategy.java b/src/main/java/de/tum/in/www1/artemis/service/team/strategies/PurgeExistingStrategy.java index ecd033670c5f..8843bbb91ad1 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/team/strategies/PurgeExistingStrategy.java +++ b/src/main/java/de/tum/in/www1/artemis/service/team/strategies/PurgeExistingStrategy.java @@ -19,7 +19,7 @@ public PurgeExistingStrategy(TeamRepository teamRepository, ParticipationService /** * Imports all teams of the source exercise into the destination exercise - * + *

    * Conflicts are prevented trivially by first deleting all teams of the destination exercise. * * @param sourceExercise Exercise from which to take the teams for the import @@ -35,7 +35,7 @@ public void importTeams(Exercise sourceExercise, Exercise destinationExercise) { /** * Imports all given teams into the destination exercise - * + *

    * Conflicts are prevented trivially by first deleting all teams of the destination exercise. * * @param exercise Exercise from which to take the teams for the import @@ -49,7 +49,7 @@ public void importTeams(Exercise exercise, List teams) { /** * Deletes all existing teams and add given teams into the exercise - * + *

    * Conflicts are prevented trivially by first deleting all teams of the destination exercise. * * @param exercise Exercise from which to take the teams for the import diff --git a/src/main/java/de/tum/in/www1/artemis/service/tutorialgroups/TutorialGroupChannelManagementService.java b/src/main/java/de/tum/in/www1/artemis/service/tutorialgroups/TutorialGroupChannelManagementService.java index ce1606485596..3ee7dbcd8328 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/tutorialgroups/TutorialGroupChannelManagementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/tutorialgroups/TutorialGroupChannelManagementService.java @@ -205,9 +205,7 @@ public void removeUsersFromTutorialGroupChannel(TutorialGroup tutorialGroup, Set */ public void removeUsersFromAllTutorialGroupChannelsInCourse(Course course, Set users) { var tutorialGroups = tutorialGroupRepository.findAllByCourseId(course.getId()); - tutorialGroups.forEach(tutorialGroup -> { - removeUsersFromTutorialGroupChannel(tutorialGroup, users); - }); + tutorialGroups.forEach(tutorialGroup -> removeUsersFromTutorialGroupChannel(tutorialGroup, users)); } /** @@ -263,6 +261,7 @@ private String determineUniqueTutorialGroupChannelName(TutorialGroup tutorialGro if (channelName.length() >= 30) { channelName = channelName.substring(0, 27); } + do { channelName += ThreadLocalRandom.current().nextInt(0, 10); } @@ -319,9 +318,7 @@ private Optional getTeachingAssistant(TutorialGroup tutorialGroup) { */ public void changeChannelModeForCourse(Course course, Boolean tutorialGroupChannelsPublic) { var channels = tutorialGroupRepository.findAllByCourseIdWithChannel(course.getId()).stream().map(this::createChannelForTutorialGroup).collect(Collectors.toSet()); - channels.forEach(channel -> { - channel.setIsPublic(tutorialGroupChannelsPublic); - }); + channels.forEach(channel -> channel.setIsPublic(tutorialGroupChannelsPublic)); channelRepository.saveAll(channels); log.debug("Changed public for all tutorial group channels of course with id {} to {}", course.getId(), tutorialGroupChannelsPublic); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/tutorialgroups/TutorialGroupFreePeriodService.java b/src/main/java/de/tum/in/www1/artemis/service/tutorialgroups/TutorialGroupFreePeriodService.java index 3d92e11cf670..7c84345d5a28 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/tutorialgroups/TutorialGroupFreePeriodService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/tutorialgroups/TutorialGroupFreePeriodService.java @@ -91,7 +91,7 @@ public void updateOverlappingSessions(Course course, TutorialGroupFreePeriod tut */ private void findAndUpdateStillCanceledSessions(Course course, TutorialGroupFreePeriod tutorialGroupFreePeriod, Set overlappingSessions, TutorialGroupFreePeriod updatedFreePeriod, boolean onDeletion) { - overlappingSessions.stream().forEach(session -> { + overlappingSessions.forEach(session -> { Set overlappingFreePeriods = tutorialGroupFreePeriodRepository.findOverlappingInSameCourseExclusive(course, session.getStart(), session.getEnd()); // if there is only one overlapping FreePeriod, the session is not still canceled diff --git a/src/main/java/de/tum/in/www1/artemis/service/tutorialgroups/TutorialGroupService.java b/src/main/java/de/tum/in/www1/artemis/service/tutorialgroups/TutorialGroupService.java index 259651aee6c7..b6c1423511b9 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/tutorialgroups/TutorialGroupService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/tutorialgroups/TutorialGroupService.java @@ -157,7 +157,7 @@ private void setNextSession(TutorialGroup tutorialGroup) { else { var nextSessions = tutorialGroupSessionRepository.findNextSessionsOfStatus(tutorialGroup.getId(), ZonedDateTime.now(), TutorialGroupSessionStatus.ACTIVE); if (!nextSessions.isEmpty()) { - nextSessionOptional = Optional.of(nextSessions.get(0)); + nextSessionOptional = Optional.of(nextSessions.getFirst()); } } nextSessionOptional.ifPresent(tutorialGroupSession -> { diff --git a/src/main/java/de/tum/in/www1/artemis/service/util/structureoraclegenerator/JavaClassDiff.java b/src/main/java/de/tum/in/www1/artemis/service/util/structureoraclegenerator/JavaClassDiff.java index 4889b5d10248..0182e1d20bdf 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/util/structureoraclegenerator/JavaClassDiff.java +++ b/src/main/java/de/tum/in/www1/artemis/service/util/structureoraclegenerator/JavaClassDiff.java @@ -22,25 +22,25 @@ public class JavaClassDiff { private final String packageName; - boolean isInterfaceDifferent; + final boolean isInterfaceDifferent; - boolean isEnumDifferent; + final boolean isEnumDifferent; - boolean isAbstractDifferent; + final boolean isAbstractDifferent; - String superClassNameDiff; + final String superClassNameDiff; - List superInterfacesDiff; + final List superInterfacesDiff; - List annotationsDiff; + final List annotationsDiff; - List attributesDiff; + final List attributesDiff; - List enumsDiff; + final List enumsDiff; - List constructorsDiff; + final List constructorsDiff; - List methodsDiff; + final List methodsDiff; JavaClassDiff(JavaClass solutionClass, JavaClass templateClass) { this.solutionClass = solutionClass; diff --git a/src/main/java/de/tum/in/www1/artemis/service/util/structureoraclegenerator/OracleGenerator.java b/src/main/java/de/tum/in/www1/artemis/service/util/structureoraclegenerator/OracleGenerator.java index 0c281f9c750c..8a37e2f0591b 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/util/structureoraclegenerator/OracleGenerator.java +++ b/src/main/java/de/tum/in/www1/artemis/service/util/structureoraclegenerator/OracleGenerator.java @@ -25,10 +25,10 @@ * It is used to automatically generate the structure oracle with the solution of the exercise as the system model and the template as the test model. * The oracle is saved in the form of a JSON file in test.json. * The structure oracle is used in the structural tests and contains information on the expected structural elements that the student has to implement. - * The generator uses the qdox framework (https://github.com/paul-hammant/qdox). + * The generator uses the qdox framework. * It extracts first the needed elements by doing a so-called diff of each element e.g. the difference between the solution of an exercise and its template. * The generator uses separate data structures that contain the elements of these diffs and then creates JSON representations of them. - * + *

    * The generator currently deals with the following structural elements: *

      *
    • Class hierarchy: abstract modifier, stereotype, declared super classes and implemented interfaces.
    • diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/AbstractSubmissionResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/AbstractSubmissionResource.java index 1a24df634641..f561f70f2306 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/AbstractSubmissionResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/AbstractSubmissionResource.java @@ -10,7 +10,6 @@ import de.tum.in.www1.artemis.repository.*; import de.tum.in.www1.artemis.security.Role; import de.tum.in.www1.artemis.service.AuthorizationCheckService; -import de.tum.in.www1.artemis.service.ResultService; import de.tum.in.www1.artemis.service.SubmissionService; /** @@ -20,8 +19,6 @@ public abstract class AbstractSubmissionResource { protected final SubmissionRepository submissionRepository; - protected final ResultService resultService; - protected final AuthorizationCheckService authCheckService; protected final UserRepository userRepository; @@ -32,11 +29,9 @@ public abstract class AbstractSubmissionResource { protected final StudentParticipationRepository studentParticipationRepository; - public AbstractSubmissionResource(SubmissionRepository submissionRepository, ResultService resultService, AuthorizationCheckService authCheckService, - UserRepository userRepository, ExerciseRepository exerciseRepository, SubmissionService submissionService, - StudentParticipationRepository studentParticipationRepository) { + public AbstractSubmissionResource(SubmissionRepository submissionRepository, AuthorizationCheckService authCheckService, UserRepository userRepository, + ExerciseRepository exerciseRepository, SubmissionService submissionService, StudentParticipationRepository studentParticipationRepository) { this.submissionRepository = submissionRepository; - this.resultService = resultService; this.exerciseRepository = exerciseRepository; this.authCheckService = authCheckService; this.userRepository = userRepository; diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/AndroidAppSiteAssociationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/AndroidAppSiteAssociationResource.java index a6f8f2adbe53..4fd5d9214308 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/AndroidAppSiteAssociationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/AndroidAppSiteAssociationResource.java @@ -33,7 +33,7 @@ public class AndroidAppSiteAssociationResource { /** * Provides the assetlinks json content for the Android client deeplink link feature. - * More information on the json content can be found here + * More information on the json content can be found here * * @return assetslinks as json */ @@ -54,7 +54,7 @@ public ResponseEntity> getAndroidAssetLinks() { return ResponseEntity.ok(List.of(handleAllUrls, getLoginCredentials)); } - record AndroidAssetLinksEntry(List relation, AndroidTarget target) { + public record AndroidAssetLinksEntry(List relation, AndroidTarget target) { record AndroidTarget(String namespace, String package_name, List sha256_cert_fingerprints) { } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/AppleAppSiteAssociationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/AppleAppSiteAssociationResource.java index 850d30e80e2b..6c2924cd99a3 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/AppleAppSiteAssociationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/AppleAppSiteAssociationResource.java @@ -28,7 +28,7 @@ public class AppleAppSiteAssociationResource { /** * Provides the apple-app-site-association json content for the iOS client universal link feature. - * More information on the json content can be found here + * More information on the json content can be found here * * @return apple-app-site-association as json */ @@ -54,7 +54,7 @@ public ResponseEntity getAppleAppSiteAssociation() { return ResponseEntity.ok(appleAppSiteAssociation); } - record AppleAppSiteAssociation(Applinks applinks, Webcredentials webcredentials) { + public record AppleAppSiteAssociation(Applinks applinks, Webcredentials webcredentials) { record Webcredentials(String[] apps) { } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/AssessmentResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/AssessmentResource.java index cfa79cd5f4be..3bb08da05466 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/AssessmentResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/AssessmentResource.java @@ -24,7 +24,6 @@ import de.tum.in.www1.artemis.security.Role; import de.tum.in.www1.artemis.service.AssessmentService; import de.tum.in.www1.artemis.service.AuthorizationCheckService; -import de.tum.in.www1.artemis.service.exam.ExamService; import de.tum.in.www1.artemis.web.rest.errors.AccessForbiddenException; import de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException; import de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException; @@ -43,20 +42,17 @@ public abstract class AssessmentResource { protected final ResultRepository resultRepository; - protected final ExamService examService; - protected final ExampleSubmissionRepository exampleSubmissionRepository; protected final SubmissionRepository submissionRepository; public AssessmentResource(AuthorizationCheckService authCheckService, UserRepository userRepository, ExerciseRepository exerciseRepository, AssessmentService assessmentService, - ResultRepository resultRepository, ExamService examService, ExampleSubmissionRepository exampleSubmissionRepository, SubmissionRepository submissionRepository) { + ResultRepository resultRepository, ExampleSubmissionRepository exampleSubmissionRepository, SubmissionRepository submissionRepository) { this.authCheckService = authCheckService; this.userRepository = userRepository; this.exerciseRepository = exerciseRepository; this.assessmentService = assessmentService; this.resultRepository = resultRepository; - this.examService = examService; this.exampleSubmissionRepository = exampleSubmissionRepository; this.submissionRepository = submissionRepository; } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/AthenaResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/AthenaResource.java index 15c1ab34382e..46e0bc300536 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/AthenaResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/AthenaResource.java @@ -135,7 +135,6 @@ public ResponseEntity> getTextFeedbackSuggestions(@PathVar @GetMapping("athena/programming-exercises/{exerciseId}/submissions/{submissionId}/feedback-suggestions") @EnforceAtLeastTutor public ResponseEntity> getProgrammingFeedbackSuggestions(@PathVariable long exerciseId, @PathVariable long submissionId) { - Exercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); return getFeedbackSuggestions(exerciseId, submissionId, programmingExerciseRepository::findByIdElseThrow, programmingSubmissionRepository::findByIdElseThrow, athenaFeedbackSuggestionsService::getProgrammingFeedbackSuggestions); } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/AttachmentResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/AttachmentResource.java index c9cea1a25963..0078b5385979 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/AttachmentResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/AttachmentResource.java @@ -68,16 +68,13 @@ public class AttachmentResource { private final FileService fileService; - private final FilePathService filePathService; - public AttachmentResource(AttachmentRepository attachmentRepository, GroupNotificationService groupNotificationService, AuthorizationCheckService authorizationCheckService, - UserRepository userRepository, FileService fileService, FilePathService filePathService) { + UserRepository userRepository, FileService fileService) { this.attachmentRepository = attachmentRepository; this.groupNotificationService = groupNotificationService; this.authorizationCheckService = authorizationCheckService; this.userRepository = userRepository; this.fileService = fileService; - this.filePathService = filePathService; } /** @@ -130,8 +127,8 @@ public ResponseEntity updateAttachment(@PathVariable Long attachment attachment.setLink(FilePathService.publicPathForActualPath(savePath, originalAttachment.getLecture().getId()).toString()); // Delete the old file URI oldPath = URI.create(originalAttachment.getLink()); - fileService.schedulePathForDeletion(filePathService.actualPathForPublicPathOrThrow(oldPath), 0); - this.fileService.evictCacheForPath(filePathService.actualPathForPublicPathOrThrow(oldPath)); + fileService.schedulePathForDeletion(FilePathService.actualPathForPublicPathOrThrow(oldPath), 0); + this.fileService.evictCacheForPath(FilePathService.actualPathForPublicPathOrThrow(oldPath)); } Attachment result = attachmentRepository.save(attachment); @@ -204,7 +201,7 @@ else if (attachment.getExercise() != null) { try { if (AttachmentType.FILE.equals(attachment.getAttachmentType())) { URI oldPath = URI.create(attachment.getLink()); - fileService.schedulePathForDeletion(filePathService.actualPathForPublicPathOrThrow(oldPath), 0); + fileService.schedulePathForDeletion(FilePathService.actualPathForPublicPathOrThrow(oldPath), 0); this.fileService.evictCacheForPath(actualPathForPublicPath(oldPath)); } } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/DataExportResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/DataExportResource.java index db5131d777b3..3ee03da3c8a9 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/DataExportResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/DataExportResource.java @@ -87,7 +87,7 @@ private boolean canRequestDataExport() { return true; } // because we order by request date desc, the first data export is the latest one - var latestDataExport = dataExports.get(0); + var latestDataExport = dataExports.getFirst(); var olderThanDaysBetweenDataExports = Duration.between(latestDataExport.getCreatedDate().atZone(ZoneId.systemDefault()), ZonedDateTime.now()) .toDays() >= DAYS_BETWEEN_DATA_EXPORTS; diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/ExamUserResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/ExamUserResource.java index ec8342c2013f..d823aeedce69 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/ExamUserResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/ExamUserResource.java @@ -46,16 +46,13 @@ public class ExamUserResource { private final ExamUserService examUserService; - private final FilePathService filePathURI; - public ExamUserResource(ExamUserService examUserService, UserRepository userRepository, FileService fileService, ExamAccessService examAccessService, - ExamUserRepository examUserRepository, FilePathService filePathURI) { + ExamUserRepository examUserRepository) { this.userRepository = userRepository; this.fileService = fileService; this.examUserRepository = examUserRepository; this.examAccessService = examAccessService; this.examUserService = examUserService; - this.filePathURI = filePathURI; } /** diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/ExerciseGroupResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/ExerciseGroupResource.java index 5fb00ff5d791..1acd218a1950 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/ExerciseGroupResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/ExerciseGroupResource.java @@ -105,7 +105,7 @@ public ResponseEntity createExerciseGroup(@PathVariable Long cour Exam examFromDB = examRepository.findByIdWithExerciseGroupsElseThrow(examId); examFromDB.addExerciseGroup(exerciseGroup); Exam savedExam = examRepository.save(examFromDB); - ExerciseGroup savedExerciseGroup = savedExam.getExerciseGroups().get(savedExam.getExerciseGroups().size() - 1); + ExerciseGroup savedExerciseGroup = savedExam.getExerciseGroups().getLast(); return ResponseEntity.created(new URI("/api/courses/" + courseId + "/exams/" + examId + "/exerciseGroups/" + savedExerciseGroup.getId())).body(savedExerciseGroup); } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/FileUploadAssessmentResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/FileUploadAssessmentResource.java index 22efd48aec72..22278ba233d2 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/FileUploadAssessmentResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/FileUploadAssessmentResource.java @@ -39,7 +39,6 @@ import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastTutor; import de.tum.in.www1.artemis.service.AssessmentService; import de.tum.in.www1.artemis.service.AuthorizationCheckService; -import de.tum.in.www1.artemis.service.exam.ExamService; /** * REST controller for managing FileUploadAssessment. @@ -59,8 +58,8 @@ public class FileUploadAssessmentResource extends AssessmentResource { public FileUploadAssessmentResource(AuthorizationCheckService authCheckService, AssessmentService assessmentService, UserRepository userRepository, FileUploadExerciseRepository fileUploadExerciseRepository, FileUploadSubmissionRepository fileUploadSubmissionRepository, ExerciseRepository exerciseRepository, - ResultRepository resultRepository, ExamService examService, ExampleSubmissionRepository exampleSubmissionRepository, SubmissionRepository submissionRepository) { - super(authCheckService, userRepository, exerciseRepository, assessmentService, resultRepository, examService, exampleSubmissionRepository, submissionRepository); + ResultRepository resultRepository, ExampleSubmissionRepository exampleSubmissionRepository, SubmissionRepository submissionRepository) { + super(authCheckService, userRepository, exerciseRepository, assessmentService, resultRepository, exampleSubmissionRepository, submissionRepository); this.fileUploadExerciseRepository = fileUploadExerciseRepository; this.fileUploadSubmissionRepository = fileUploadSubmissionRepository; } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/FileUploadSubmissionResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/FileUploadSubmissionResource.java index ec86aac89cb5..10a4d75e0ef9 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/FileUploadSubmissionResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/FileUploadSubmissionResource.java @@ -67,7 +67,7 @@ public FileUploadSubmissionResource(SubmissionRepository submissionRepository, R ExerciseRepository exerciseRepository, GradingCriterionRepository gradingCriterionRepository, ExamSubmissionService examSubmissionService, StudentParticipationRepository studentParticipationRepository, FileUploadSubmissionRepository fileUploadSubmissionRepository, SingleUserNotificationService singleUserNotificationService) { - super(submissionRepository, resultService, authCheckService, userRepository, exerciseRepository, fileUploadSubmissionService, studentParticipationRepository); + super(submissionRepository, authCheckService, userRepository, exerciseRepository, fileUploadSubmissionService, studentParticipationRepository); this.fileUploadSubmissionService = fileUploadSubmissionService; this.fileUploadExerciseRepository = fileUploadExerciseRepository; this.gradingCriterionRepository = gradingCriterionRepository; diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingAssessmentResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingAssessmentResource.java index 5b05f6eb077d..f293fccc0fff 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingAssessmentResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingAssessmentResource.java @@ -40,7 +40,6 @@ import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastTutor; import de.tum.in.www1.artemis.service.AssessmentService; import de.tum.in.www1.artemis.service.AuthorizationCheckService; -import de.tum.in.www1.artemis.service.exam.ExamService; import de.tum.in.www1.artemis.web.rest.errors.ErrorConstants; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; @@ -69,8 +68,8 @@ public class ModelingAssessmentResource extends AssessmentResource { public ModelingAssessmentResource(AuthorizationCheckService authCheckService, UserRepository userRepository, ModelingExerciseRepository modelingExerciseRepository, AssessmentService assessmentService, ModelingSubmissionRepository modelingSubmissionRepository, ExampleSubmissionRepository exampleSubmissionRepository, - ExerciseRepository exerciseRepository, ResultRepository resultRepository, ExamService examService, SubmissionRepository submissionRepository) { - super(authCheckService, userRepository, exerciseRepository, assessmentService, resultRepository, examService, exampleSubmissionRepository, submissionRepository); + ExerciseRepository exerciseRepository, ResultRepository resultRepository, SubmissionRepository submissionRepository) { + super(authCheckService, userRepository, exerciseRepository, assessmentService, resultRepository, exampleSubmissionRepository, submissionRepository); this.modelingExerciseRepository = modelingExerciseRepository; this.authCheckService = authCheckService; this.modelingSubmissionRepository = modelingSubmissionRepository; diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingSubmissionResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingSubmissionResource.java index fbdad024d5dc..bfa721050094 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingSubmissionResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingSubmissionResource.java @@ -68,7 +68,7 @@ public ModelingSubmissionResource(SubmissionRepository submissionRepository, Res ModelingExerciseRepository modelingExerciseRepository, AuthorizationCheckService authCheckService, UserRepository userRepository, ExerciseRepository exerciseRepository, GradingCriterionRepository gradingCriterionRepository, ExamSubmissionService examSubmissionService, StudentParticipationRepository studentParticipationRepository, ModelingSubmissionRepository modelingSubmissionRepository, PlagiarismService plagiarismService) { - super(submissionRepository, resultService, authCheckService, userRepository, exerciseRepository, modelingSubmissionService, studentParticipationRepository); + super(submissionRepository, authCheckService, userRepository, exerciseRepository, modelingSubmissionService, studentParticipationRepository); this.modelingSubmissionService = modelingSubmissionService; this.modelingExerciseRepository = modelingExerciseRepository; this.gradingCriterionRepository = gradingCriterionRepository; diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/NotificationSettingsResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/NotificationSettingsResource.java index c1bcdd5c4024..d251de8d4a99 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/NotificationSettingsResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/NotificationSettingsResource.java @@ -54,7 +54,7 @@ public NotificationSettingsResource(NotificationSettingRepository notificationSe /** * GET notification-settings : Get all NotificationSettings for current user - * + *

      * Fetches the NotificationSettings for the current user from the server. * If the user has not yet modified the settings there will be none in the database, then * @@ -74,7 +74,7 @@ public ResponseEntity> getNotificationSettingsForCurren /** * PUT notification-settings : Save NotificationSettings for current user - * + *

      * Saves the provided NotificationSettings to the server. * * @param notificationSettings which should be saved to the notificationSetting database. diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/QuizExerciseResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/QuizExerciseResource.java index 7bcadb2377e4..c0055638afe5 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/QuizExerciseResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/QuizExerciseResource.java @@ -314,7 +314,7 @@ public ResponseEntity> getQuizExercisesForCourse(@PathVariabl public ResponseEntity> getQuizExercisesForExam(@PathVariable Long examId) { log.info("REST request to get all quiz exercises for the exam with id : {}", examId); List quizExercises = quizExerciseRepository.findByExamId(examId); - Course course = quizExercises.get(0).getCourseViaExerciseGroupOrCourseMember(); + Course course = quizExercises.getFirst().getCourseViaExerciseGroupOrCourseMember(); authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, course, null); for (QuizExercise quizExercise : quizExercises) { diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/SubmissionResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/SubmissionResource.java index 592d8192304f..bd081a4afbe6 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/SubmissionResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/SubmissionResource.java @@ -153,8 +153,8 @@ public ResponseEntity> getTestRunSubmissionsForAssessment(@Path var testRunParticipations = studentParticipationRepository.findTestRunParticipationsByStudentIdAndIndividualExercisesWithEagerSubmissionsResult(user.getId(), List.of(exercise)); - if (!testRunParticipations.isEmpty() && testRunParticipations.get(0).findLatestSubmission().isPresent()) { - var latestSubmission = testRunParticipations.get(0).findLatestSubmission().get(); + if (!testRunParticipations.isEmpty() && testRunParticipations.getFirst().findLatestSubmission().isPresent()) { + var latestSubmission = testRunParticipations.getFirst().findLatestSubmission().get(); if (latestSubmission.getManualResults().isEmpty()) { latestSubmission.addResult(submissionService.prepareTestRunSubmissionForAssessment(latestSubmission)); } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/TextAssessmentResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/TextAssessmentResource.java index 3b48e5ac9b76..8ea430c45199 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/TextAssessmentResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/TextAssessmentResource.java @@ -60,7 +60,6 @@ import de.tum.in.www1.artemis.service.TextBlockService; import de.tum.in.www1.artemis.service.TextSubmissionService; import de.tum.in.www1.artemis.service.connectors.athena.AthenaFeedbackSendingService; -import de.tum.in.www1.artemis.service.exam.ExamService; import de.tum.in.www1.artemis.web.rest.dto.TextAssessmentDTO; import de.tum.in.www1.artemis.web.rest.dto.TextAssessmentUpdateDTO; import de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException; @@ -107,10 +106,9 @@ public class TextAssessmentResource extends AssessmentResource { public TextAssessmentResource(AuthorizationCheckService authCheckService, TextAssessmentService textAssessmentService, TextBlockService textBlockService, TextExerciseRepository textExerciseRepository, TextSubmissionRepository textSubmissionRepository, UserRepository userRepository, TextSubmissionService textSubmissionService, ExerciseRepository exerciseRepository, ResultRepository resultRepository, - GradingCriterionRepository gradingCriterionRepository, ExamService examService, ExampleSubmissionRepository exampleSubmissionRepository, - SubmissionRepository submissionRepository, FeedbackRepository feedbackRepository, ResultService resultService, - Optional athenaFeedbackSendingService) { - super(authCheckService, userRepository, exerciseRepository, textAssessmentService, resultRepository, examService, exampleSubmissionRepository, submissionRepository); + GradingCriterionRepository gradingCriterionRepository, ExampleSubmissionRepository exampleSubmissionRepository, SubmissionRepository submissionRepository, + FeedbackRepository feedbackRepository, ResultService resultService, Optional athenaFeedbackSendingService) { + super(authCheckService, userRepository, exerciseRepository, textAssessmentService, resultRepository, exampleSubmissionRepository, submissionRepository); this.textAssessmentService = textAssessmentService; this.textBlockService = textBlockService; diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/TextSubmissionResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/TextSubmissionResource.java index c5b5610f89a8..c678ccf8db85 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/TextSubmissionResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/TextSubmissionResource.java @@ -67,7 +67,7 @@ public TextSubmissionResource(SubmissionRepository submissionRepository, ResultS TextSubmissionService textSubmissionService, UserRepository userRepository, StudentParticipationRepository studentParticipationRepository, GradingCriterionRepository gradingCriterionRepository, TextAssessmentService textAssessmentService, ExamSubmissionService examSubmissionService, PlagiarismService plagiarismService) { - super(submissionRepository, resultService, authCheckService, userRepository, exerciseRepository, textSubmissionService, studentParticipationRepository); + super(submissionRepository, authCheckService, userRepository, exerciseRepository, textSubmissionService, studentParticipationRepository); this.textSubmissionRepository = textSubmissionRepository; this.exerciseRepository = exerciseRepository; this.textExerciseRepository = textExerciseRepository; diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/admin/AdminOrganizationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/admin/AdminOrganizationResource.java index 2611f689df5a..cdeda1c2d03b 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/admin/AdminOrganizationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/admin/AdminOrganizationResource.java @@ -112,7 +112,7 @@ public ResponseEntity addUserToOrganization(@PathVariable String userLogin /** * DELETE organizations/:organizationId/users/:userLogin : * Remove a user from an organization - * + *

      * Keep in mind that removing a user from an organization does not remove it * from the Access Groups of a course if already added. * diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/errors/ExceptionTranslator.java b/src/main/java/de/tum/in/www1/artemis/web/rest/errors/ExceptionTranslator.java index 845a8033c865..6c10ea1ccd2c 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/errors/ExceptionTranslator.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/errors/ExceptionTranslator.java @@ -34,8 +34,8 @@ import tech.jhipster.web.util.HeaderUtil; /** - * Controller advice to translate the server side exceptions to client-friendly json structures. The error response follows RFC7807 - Problem Details for HTTP APIs - * (https://tools.ietf.org/html/rfc7807) + * Controller advice to translate the server side exceptions to client-friendly json structures. The error response follows + * RFC7807 - Problem Details for HTTP APIs */ @ControllerAdvice public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait { diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/hestia/CoverageReportResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/hestia/CoverageReportResource.java index 055262266c43..ae6ccdd1b746 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/hestia/CoverageReportResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/hestia/CoverageReportResource.java @@ -12,9 +12,9 @@ import org.springframework.web.bind.annotation.RestController; import de.tum.in.www1.artemis.domain.hestia.CoverageReport; -import de.tum.in.www1.artemis.repository.ProgrammingExerciseRepository; import de.tum.in.www1.artemis.security.annotations.enforceRoleInExercise.EnforceAtLeastTutorInExercise; import de.tum.in.www1.artemis.service.hestia.TestwiseCoverageService; +import de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException; /** * REST controller for managing ProgrammingExerciseTestwiseCoverageReports and its entries. @@ -28,11 +28,8 @@ public class CoverageReportResource { private final TestwiseCoverageService testwiseCoverageService; - private final ProgrammingExerciseRepository programmingExerciseRepository; - - public CoverageReportResource(TestwiseCoverageService testwiseCoverageService, ProgrammingExerciseRepository programmingExerciseRepository) { + public CoverageReportResource(TestwiseCoverageService testwiseCoverageService) { this.testwiseCoverageService = testwiseCoverageService; - this.programmingExerciseRepository = programmingExerciseRepository; } /** @@ -47,11 +44,9 @@ public CoverageReportResource(TestwiseCoverageService testwiseCoverageService, P public ResponseEntity getLatestFullCoverageReport(@PathVariable Long exerciseId) { log.debug("REST request to get the latest Full Testwise CoverageReport for exercise {}", exerciseId); - var optionalReportWithFileReports = testwiseCoverageService.getFullCoverageReportForLatestSolutionSubmissionFromProgrammingExercise(exerciseId); - if (optionalReportWithFileReports.isEmpty()) { - return ResponseEntity.notFound().build(); - } - return ResponseEntity.ok(optionalReportWithFileReports.get()); + var optionalReportWithFileReports = testwiseCoverageService.getFullCoverageReportForLatestSolutionSubmissionFromProgrammingExercise(exerciseId) + .orElseThrow(() -> new EntityNotFoundException("Coverage report for exercise " + exerciseId + " not found.")); + return ResponseEntity.ok(optionalReportWithFileReports); } /** @@ -66,10 +61,8 @@ public ResponseEntity getLatestFullCoverageReport(@PathVariable public ResponseEntity getLatestCoverageReport(@PathVariable Long exerciseId) { log.debug("REST request to get the latest Testwise CoverageReport for exercise {}", exerciseId); - var optionalReportWithoutFileReports = testwiseCoverageService.getCoverageReportForLatestSolutionSubmissionFromProgrammingExercise(exerciseId); - if (optionalReportWithoutFileReports.isEmpty()) { - return ResponseEntity.notFound().build(); - } - return ResponseEntity.ok(optionalReportWithoutFileReports.get()); + var optionalReportWithoutFileReports = testwiseCoverageService.getCoverageReportForLatestSolutionSubmissionFromProgrammingExercise(exerciseId) + .orElseThrow(() -> new EntityNotFoundException("Coverage report for exercise " + exerciseId + " not found.")); + return ResponseEntity.ok(optionalReportWithoutFileReports); } } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/ExerciseUnitResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/ExerciseUnitResource.java index 83dea24c103c..86230639b480 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/ExerciseUnitResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/ExerciseUnitResource.java @@ -71,7 +71,7 @@ public ResponseEntity createExerciseUnit(@PathVariable Long lectur exerciseUnit.setLecture(lecture); lecture.addLectureUnit(exerciseUnit); Lecture updatedLecture = lectureRepository.save(lecture); - ExerciseUnit persistedExerciseUnit = (ExerciseUnit) updatedLecture.getLectureUnits().get(updatedLecture.getLectureUnits().size() - 1); + ExerciseUnit persistedExerciseUnit = (ExerciseUnit) updatedLecture.getLectureUnits().getLast(); return ResponseEntity.created(new URI("/api/exercise-units/" + persistedExerciseUnit.getId())).body(persistedExerciseUnit); } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/OnlineUnitResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/OnlineUnitResource.java index 524300dd7b63..e5acc62bde19 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/OnlineUnitResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/OnlineUnitResource.java @@ -3,7 +3,6 @@ import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE; import java.io.IOException; -import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -35,6 +34,7 @@ import de.tum.in.www1.artemis.security.Role; import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastEditor; import de.tum.in.www1.artemis.service.AuthorizationCheckService; +import de.tum.in.www1.artemis.service.LectureUnitService; import de.tum.in.www1.artemis.service.competency.CompetencyProgressService; import de.tum.in.www1.artemis.web.rest.dto.OnlineResourceDTO; import de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException; @@ -57,12 +57,15 @@ public class OnlineUnitResource { private final CompetencyProgressService competencyProgressService; + private final LectureUnitService lectureUnitService; + public OnlineUnitResource(LectureRepository lectureRepository, AuthorizationCheckService authorizationCheckService, OnlineUnitRepository onlineUnitRepository, - CompetencyProgressService competencyProgressService) { + CompetencyProgressService competencyProgressService, LectureUnitService lectureUnitService) { this.lectureRepository = lectureRepository; this.authorizationCheckService = authorizationCheckService; this.onlineUnitRepository = onlineUnitRepository; this.competencyProgressService = competencyProgressService; + this.lectureUnitService = lectureUnitService; } /** @@ -98,7 +101,7 @@ public ResponseEntity updateOnlineUnit(@PathVariable Long lectureId, } checkOnlineUnitCourseAndLecture(onlineUnit, lectureId); - validateUrl(onlineUnit); + lectureUnitService.validateUrlStringAndReturnUrl(onlineUnit.getSource()); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, onlineUnit.getLecture().getCourse(), null); @@ -123,7 +126,7 @@ public ResponseEntity createOnlineUnit(@PathVariable Long lectureId, throw new BadRequestException(); } - validateUrl(onlineUnit); + lectureUnitService.validateUrlStringAndReturnUrl(onlineUnit.getSource()); Lecture lecture = lectureRepository.findByIdWithLectureUnitsAndAttachmentsElseThrow(lectureId); if (lecture.getCourse() == null) { @@ -153,20 +156,20 @@ public ResponseEntity createOnlineUnit(@PathVariable Long lectureId, @GetMapping("lectures/online-units/fetch-online-resource") @EnforceAtLeastEditor public ResponseEntity getOnlineResource(@RequestParam("link") String link) { - try { - // Ensure that the link is a correctly formed URL - URL url = new URL(link); + // Ensure that the link is a correctly formed URL + URL url = lectureUnitService.validateUrlStringAndReturnUrl(link); - if (!"http".equalsIgnoreCase(url.getProtocol()) && !"https".equalsIgnoreCase(url.getProtocol())) { - throw new BadRequestException("The specified link uses an unsupported protocol"); - } + if (!"http".equalsIgnoreCase(url.getProtocol()) && !"https".equalsIgnoreCase(url.getProtocol())) { + throw new BadRequestException("The specified link uses an unsupported protocol"); + } - if (!InternetDomainName.isValid(url.getHost()) || "localhost".equalsIgnoreCase(url.getHost())) { - throw new BadRequestException("The specified link does not contain a valid domain"); - } + if (!InternetDomainName.isValid(url.getHost()) || "localhost".equalsIgnoreCase(url.getHost())) { + throw new BadRequestException("The specified link does not contain a valid domain"); + } - log.info("Requesting online resource at {}", url); + log.info("Requesting online resource at {}", url); + try { // Request the document, limited to 3 seconds and 500 KB (enough for most websites) Document document = Jsoup.connect(url.toString()).timeout(3000).maxBodySize(500000).get(); String title = getMetaTagContent(document, "title"); @@ -174,9 +177,6 @@ public ResponseEntity getOnlineResource(@RequestParam("link") return ResponseEntity.ok(new OnlineResourceDTO(url.toString(), title, description)); } - catch (MalformedURLException e) { - throw new BadRequestException("The specified link is not a valid URL"); - } catch (IOException e) { throw new InternalServerErrorException("Error while retrieving metadata from link"); } @@ -202,20 +202,6 @@ private String getMetaTagContent(Document document, String tag) { return ""; } - /** - * Validates the source url of an online unit. - * - * @param onlineUnit The online unit to check the source URL for. - */ - private void validateUrl(OnlineUnit onlineUnit) { - try { - new URL(onlineUnit.getSource()); - } - catch (MalformedURLException exception) { - throw new BadRequestException(); - } - } - /** * Checks that the online unit belongs to the specified lecture. * diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/TextUnitResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/TextUnitResource.java index cbf99e59b98c..f724cd3e60c0 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/TextUnitResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/TextUnitResource.java @@ -131,7 +131,7 @@ public ResponseEntity createTextUnit(@PathVariable Long lectureId, @Re textUnit.setLecture(lecture); lecture.addLectureUnit(textUnit); Lecture updatedLecture = lectureRepository.save(lecture); - TextUnit persistedTextUnit = (TextUnit) updatedLecture.getLectureUnits().get(updatedLecture.getLectureUnits().size() - 1); + TextUnit persistedTextUnit = (TextUnit) updatedLecture.getLectureUnits().getLast(); competencyProgressService.updateProgressByLearningObjectAsync(persistedTextUnit); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/VideoUnitResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/VideoUnitResource.java index 69b297e9eb80..1076429bc561 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/VideoUnitResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/lecture/VideoUnitResource.java @@ -2,10 +2,8 @@ import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE; -import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; import jakarta.ws.rs.BadRequestException; @@ -22,6 +20,7 @@ import de.tum.in.www1.artemis.security.Role; import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastEditor; import de.tum.in.www1.artemis.service.AuthorizationCheckService; +import de.tum.in.www1.artemis.service.LectureUnitService; import de.tum.in.www1.artemis.service.competency.CompetencyProgressService; import de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException; @@ -42,12 +41,15 @@ public class VideoUnitResource { private final CompetencyProgressService competencyProgressService; + private final LectureUnitService lectureUnitService; + public VideoUnitResource(LectureRepository lectureRepository, AuthorizationCheckService authorizationCheckService, VideoUnitRepository videoUnitRepository, - CompetencyProgressService competencyProgressService) { + CompetencyProgressService competencyProgressService, LectureUnitService lectureUnitService) { this.lectureRepository = lectureRepository; this.authorizationCheckService = authorizationCheckService; this.videoUnitRepository = videoUnitRepository; this.competencyProgressService = competencyProgressService; + this.lectureUnitService = lectureUnitService; } /** @@ -84,7 +86,7 @@ public ResponseEntity updateVideoUnit(@PathVariable Long lectureId, @ checkVideoUnitCourseAndLecture(videoUnit, lectureId); normalizeVideoUrl(videoUnit); - validateVideoUrl(videoUnit); + lectureUnitService.validateUrlStringAndReturnUrl(videoUnit.getSource()); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, videoUnit.getLecture().getCourse(), null); VideoUnit result = videoUnitRepository.save(videoUnit); @@ -111,7 +113,7 @@ public ResponseEntity createVideoUnit(@PathVariable Long lectureId, @ } normalizeVideoUrl(videoUnit); - validateVideoUrl(videoUnit); + lectureUnitService.validateUrlStringAndReturnUrl(videoUnit.getSource()); Lecture lecture = lectureRepository.findByIdWithLectureUnitsAndAttachmentsElseThrow(lectureId); if (lecture.getCourse() == null) { @@ -125,7 +127,7 @@ public ResponseEntity createVideoUnit(@PathVariable Long lectureId, @ videoUnit.setLecture(lecture); lecture.addLectureUnit(videoUnit); Lecture updatedLecture = lectureRepository.save(lecture); - VideoUnit persistedVideoUnit = (VideoUnit) updatedLecture.getLectureUnits().get(updatedLecture.getLectureUnits().size() - 1); + VideoUnit persistedVideoUnit = (VideoUnit) updatedLecture.getLectureUnits().getLast(); competencyProgressService.updateProgressByLearningObjectAsync(persistedVideoUnit); @@ -158,19 +160,4 @@ private void normalizeVideoUrl(VideoUnit videoUnit) { videoUnit.setSource(videoUnit.getSource().strip()); } } - - /** - * Validates the provided video Url. - * - * @param videoUnit provided video unit - */ - private void validateVideoUrl(VideoUnit videoUnit) { - try { - new URL(videoUnit.getSource()); - } - catch (MalformedURLException exception) { - throw new BadRequestException(); - } - } - } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/GroupChatResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/GroupChatResource.java index fce414b86eb8..33f1209d9852 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/GroupChatResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/GroupChatResource.java @@ -137,7 +137,7 @@ public ResponseEntity registerUsersToGroupChat(@PathVariable Long courseId log.debug("REST request to register {} users to group chat: {}", userLogins.size(), groupChatId); var course = courseRepository.findByIdElseThrow(courseId); checkMessagingEnabledElseThrow(course); - if (userLogins == null || userLogins.isEmpty()) { + if (userLogins.isEmpty()) { throw new BadRequestAlertException("No user logins provided", GROUP_CHAT_ENTITY_NAME, "userLoginsEmpty"); } var groupChatFromDatabase = groupChatRepository.findByIdElseThrow(groupChatId); @@ -165,7 +165,7 @@ public ResponseEntity deregisterUsersFromGroupChat(@PathVariable Long cour log.debug("REST request to deregister {} users from the group chat : {}", userLogins.size(), groupChatId); var course = courseRepository.findByIdElseThrow(courseId); checkMessagingEnabledElseThrow(course); - if (userLogins == null || userLogins.isEmpty()) { + if (userLogins.isEmpty()) { throw new BadRequestAlertException("No user logins provided", GROUP_CHAT_ENTITY_NAME, "userLoginsEmpty"); } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicAccountResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicAccountResource.java index 313a97c39bf4..e0fd3b6c8d4f 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicAccountResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicAccountResource.java @@ -206,9 +206,9 @@ public ResponseEntity requestPasswordReset(@RequestBody String mailUsernam else if (internalUsers.size() >= 2) { throw new BadRequestAlertException("Email or username is not unique. Found multiple potential users", "Account", "usernameNotUnique"); } - var internalUser = internalUsers.get(0); + var internalUser = internalUsers.getFirst(); if (userService.prepareUserForPasswordReset(internalUser)) { - mailService.sendPasswordResetMail(internalUsers.get(0)); + mailService.sendPasswordResetMail(internalUsers.getFirst()); } } else { diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicLtiResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicLtiResource.java index 49e142cbded0..89ae1578b692 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicLtiResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicLtiResource.java @@ -40,7 +40,7 @@ public class PublicLtiResource { /** * POST lti13/auth-callback Redirects an LTI 1.3 Authorization Request Response to the client * POST lti13/deep-link: Redirects an LTI 1.3 Deep Linking Request Response to the client - * + *

      * Consolidates handling for both 'auth-callback' and 'deep-link' endpoints to simplify client interactions. * This approach ensures consistent processing and user experience for authentication and deep linking flows. * @@ -88,10 +88,7 @@ public ResponseEntity lti13LaunchRedirect(HttpServletRequest request, Http private boolean isValidJwtIgnoreSignature(String token) { try { SignedJWT parsedToken = SignedJWT.parse(token); - if (parsedToken.getJWTClaimsSet().getExpirationTime().before(Date.from(Instant.now()))) { - return false; - } - return true; + return !parsedToken.getJWTClaimsSet().getExpirationTime().before(Date.from(Instant.now())); } catch (ParseException e) { log.info("LTI request: JWT token is invalid: {}", token, e); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/plagiarism/PlagiarismCaseResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/plagiarism/PlagiarismCaseResource.java index d3078934f6aa..671cbff7f456 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/plagiarism/PlagiarismCaseResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/plagiarism/PlagiarismCaseResource.java @@ -102,7 +102,7 @@ public ResponseEntity> getPlagiarismCasesForExamForInstruct var plagiarismCases = plagiarismCaseRepository.findByExamIdWithPlagiarismSubmissionsAndComparison(examId); if (!plagiarismCases.isEmpty()) { - var plagiarismCase = plagiarismCases.get(0); + var plagiarismCase = plagiarismCases.getFirst(); var exam = plagiarismCase.getExercise().getExerciseGroup().getExam(); if (!exam.getCourse().getId().equals(courseId)) { throw new ConflictException("Exam with id " + exam.getId() + " is not related to the given course id " + courseId, ENTITY_NAME, "courseMismatch"); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingAssessmentResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingAssessmentResource.java index b6252949e932..18efd66787f0 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingAssessmentResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingAssessmentResource.java @@ -64,7 +64,7 @@ public class ProgrammingAssessmentResource extends AssessmentResource { public ProgrammingAssessmentResource(AuthorizationCheckService authCheckService, UserRepository userRepository, ProgrammingAssessmentService programmingAssessmentService, ProgrammingSubmissionRepository programmingSubmissionRepository, ExerciseRepository exerciseRepository, ResultRepository resultRepository, ExamService examService, StudentParticipationRepository studentParticipationRepository, ExampleSubmissionRepository exampleSubmissionRepository, SubmissionRepository submissionRepository) { - super(authCheckService, userRepository, exerciseRepository, programmingAssessmentService, resultRepository, examService, exampleSubmissionRepository, submissionRepository); + super(authCheckService, userRepository, exerciseRepository, programmingAssessmentService, resultRepository, exampleSubmissionRepository, submissionRepository); this.programmingAssessmentService = programmingAssessmentService; this.programmingSubmissionRepository = programmingSubmissionRepository; this.studentParticipationRepository = studentParticipationRepository; diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/tutorialgroups/TutorialGroupResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/tutorialgroups/TutorialGroupResource.java index 494ea6d9a254..1ecf1169ba4d 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/tutorialgroups/TutorialGroupResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/tutorialgroups/TutorialGroupResource.java @@ -297,7 +297,7 @@ public record TutorialGroupUpdateDTO(@Valid @NotNull TutorialGroup tutorialGroup @PutMapping("courses/{courseId}/tutorial-groups/{tutorialGroupId}") @EnforceAtLeastInstructor @FeatureToggle(Feature.TutorialGroups) - public ResponseEntity update(@PathVariable Long courseId, @PathVariable Long tutorialGroupId, + public ResponseEntity update(@PathVariable long courseId, @PathVariable long tutorialGroupId, @RequestBody @Valid TutorialGroupUpdateDTO tutorialGroupUpdateDTO) { TutorialGroup updatedTutorialGroup = tutorialGroupUpdateDTO.tutorialGroup(); log.debug("REST request to update TutorialGroup : {}", updatedTutorialGroup); @@ -306,7 +306,7 @@ public ResponseEntity update(@PathVariable Long courseId, @PathVa } var oldTutorialGroup = this.tutorialGroupRepository.findByIdWithTeachingAssistantAndRegistrationsElseThrow(tutorialGroupId); updatedTutorialGroup.setCourse(oldTutorialGroup.getCourse()); - checkEntityIdMatchesPathIds(oldTutorialGroup, Optional.ofNullable(courseId), Optional.ofNullable(tutorialGroupId)); + checkEntityIdMatchesPathIds(oldTutorialGroup, Optional.of(courseId), Optional.of(tutorialGroupId)); var responsibleUser = userRepository.getUserWithGroupsAndAuthorities(); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.INSTRUCTOR, oldTutorialGroup.getCourse(), responsibleUser); @@ -359,7 +359,7 @@ public ResponseEntity update(@PathVariable Long courseId, @PathVa if (StringUtils.hasText(tutorialGroupUpdateDTO.notificationText())) { tutorialGroupNotificationService.notifyAboutTutorialGroupUpdate(oldTutorialGroup, updatedTutorialGroup.getTeachingAssistant() == null || !updatedTutorialGroup.getTeachingAssistant().equals(responsibleUser), - StringUtils.trimWhitespace(tutorialGroupUpdateDTO.notificationText())); + tutorialGroupUpdateDTO.notificationText().strip()); } overrideValues(updatedTutorialGroup, oldTutorialGroup); @@ -434,13 +434,13 @@ public ResponseEntity registerStudent(@PathVariable Long courseId, @PathVa @PostMapping("courses/{courseId}/tutorial-groups/{tutorialGroupId}/register-multiple") @EnforceAtLeastInstructor @FeatureToggle(Feature.TutorialGroups) - public ResponseEntity> registerMultipleStudentsToTutorialGroup(@PathVariable Long courseId, @PathVariable Long tutorialGroupId, + public ResponseEntity> registerMultipleStudentsToTutorialGroup(@PathVariable long courseId, @PathVariable long tutorialGroupId, @RequestBody Set studentDtos) { log.debug("REST request to register {} to tutorial group {}", studentDtos, tutorialGroupId); var tutorialGroupFromDatabase = this.tutorialGroupRepository.findByIdElseThrow(tutorialGroupId); var responsibleUser = userRepository.getUserWithGroupsAndAuthorities(); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.INSTRUCTOR, tutorialGroupFromDatabase.getCourse(), responsibleUser); - checkEntityIdMatchesPathIds(tutorialGroupFromDatabase, Optional.ofNullable(courseId), Optional.ofNullable(tutorialGroupId)); + checkEntityIdMatchesPathIds(tutorialGroupFromDatabase, Optional.of(courseId), Optional.of(tutorialGroupId)); Set notFoundStudentDtos = tutorialGroupService.registerMultipleStudents(tutorialGroupFromDatabase, studentDtos, TutorialGroupRegistrationType.INSTRUCTOR_REGISTRATION, responsibleUser); return ResponseEntity.ok().body(notFoundStudentDtos); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/tutorialgroups/TutorialGroupSessionResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/tutorialgroups/TutorialGroupSessionResource.java index db1d58fa3757..19c0172e3542 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/tutorialgroups/TutorialGroupSessionResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/tutorialgroups/TutorialGroupSessionResource.java @@ -264,7 +264,7 @@ public ResponseEntity cancel(@PathVariable Long courseId, checkEntityIdMatchesPathIds(sessionToCancel, Optional.ofNullable(courseId), Optional.ofNullable(tutorialGroupId), Optional.of(sessionId)); tutorialGroupService.isAllowedToModifySessionsOfTutorialGroup(sessionToCancel.getTutorialGroup(), null); sessionToCancel.setStatus(TutorialGroupSessionStatus.CANCELLED); - if (tutorialGroupStatusDTO != null && tutorialGroupStatusDTO.status_explanation() != null && tutorialGroupStatusDTO.status_explanation().trim().length() > 0) { + if (tutorialGroupStatusDTO != null && tutorialGroupStatusDTO.status_explanation() != null && !tutorialGroupStatusDTO.status_explanation().trim().isEmpty()) { sessionToCancel.setStatusExplanation(tutorialGroupStatusDTO.status_explanation().trim()); } sessionToCancel = tutorialGroupSessionRepository.save(sessionToCancel); @@ -282,13 +282,13 @@ public ResponseEntity cancel(@PathVariable Long courseId, @PostMapping("courses/{courseId}/tutorial-groups/{tutorialGroupId}/sessions/{sessionId}/activate") @EnforceAtLeastTutor @FeatureToggle(Feature.TutorialGroups) - public ResponseEntity activate(@PathVariable Long courseId, @PathVariable Long tutorialGroupId, @PathVariable Long sessionId) throws URISyntaxException { + public ResponseEntity activate(@PathVariable long courseId, @PathVariable long tutorialGroupId, @PathVariable long sessionId) throws URISyntaxException { log.debug("REST request to activate session: {} of tutorial group: {} of course {}", sessionId, tutorialGroupId, courseId); var sessionToActivate = tutorialGroupSessionRepository.findByIdElseThrow(sessionId); if (sessionToActivate.getTutorialGroupFreePeriod() != null) { throw new BadRequestException("You can not activate a session that is cancelled by a overlapping with a free period"); } - checkEntityIdMatchesPathIds(sessionToActivate, Optional.ofNullable(courseId), Optional.ofNullable(tutorialGroupId), Optional.ofNullable(sessionId)); + checkEntityIdMatchesPathIds(sessionToActivate, Optional.of(courseId), Optional.of(tutorialGroupId), Optional.of(sessionId)); tutorialGroupService.isAllowedToModifySessionsOfTutorialGroup(sessionToActivate.getTutorialGroup(), null); sessionToActivate.setStatus(TutorialGroupSessionStatus.ACTIVE); sessionToActivate.setStatusExplanation(null); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/util/StringUtil.java b/src/main/java/de/tum/in/www1/artemis/web/rest/util/StringUtil.java index dd6b3132e072..829669f16657 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/util/StringUtil.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/util/StringUtil.java @@ -7,7 +7,7 @@ */ public class StringUtil { - public static String ILLEGAL_CHARACTERS = "#%&{}\\<>*?/$!'\":@+`|=."; + public static final String ILLEGAL_CHARACTERS = "#%&{}\\<>*?/$!'\":@+`|=."; /** * Removes all chars from ILLEGAL_CHARACTERS from the input String diff --git a/src/main/java/de/tum/in/www1/artemis/web/websocket/team/ParticipationTeamWebsocketService.java b/src/main/java/de/tum/in/www1/artemis/web/websocket/team/ParticipationTeamWebsocketService.java index 9390b316e1ec..96d9749ae567 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/websocket/team/ParticipationTeamWebsocketService.java +++ b/src/main/java/de/tum/in/www1/artemis/web/websocket/team/ParticipationTeamWebsocketService.java @@ -100,7 +100,7 @@ public void init() { /** * Called when a user subscribes to the destination specified in the subscribe mapping - * + *

      * We have to keep track of the destination that this session belongs to since it is * needed on unsubscribe and disconnect but is not available there. * diff --git a/src/main/webapp/app/course/tutorial-groups/services/tutorial-group-session.service.ts b/src/main/webapp/app/course/tutorial-groups/services/tutorial-group-session.service.ts index c990ff95078e..122a98d6ac52 100644 --- a/src/main/webapp/app/course/tutorial-groups/services/tutorial-group-session.service.ts +++ b/src/main/webapp/app/course/tutorial-groups/services/tutorial-group-session.service.ts @@ -23,6 +23,7 @@ export class TutorialGroupSessionService { private httpClient: HttpClient, private tutorialGroupFreePeriodService: TutorialGroupFreePeriodService, ) {} + getOneOfTutorialGroup(courseId: number, tutorialGroupId: number, sessionId: number) { return this.httpClient .get(`${this.resourceURL}/courses/${courseId}/tutorial-groups/${tutorialGroupId}/sessions/${sessionId}`, { observe: 'response' }) diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html index af07fcf95745..a4c1272ab2e4 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html @@ -13,7 +13,14 @@

      {{ tutorialGroup.title }}

      @if (sessions && sessions.length) {
      - +
      } @else { {{ '-' }} diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts index 64fc302fbc36..537cfaef5cf5 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts @@ -4,11 +4,13 @@ import { Course, isMessagingEnabled } from 'app/entities/course.model'; import { SafeHtml } from '@angular/platform-browser'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { getDayTranslationKey } from '../weekdays'; -import { TutorialGroupSession } from 'app/entities/tutorial-group/tutorial-group-session.model'; +import { TutorialGroupSession, TutorialGroupSessionStatus } from 'app/entities/tutorial-group/tutorial-group-session.model'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { DetailOverviewSection, DetailType } from 'app/detail-overview-list/detail-overview-list.component'; import { TranslateService } from '@ngx-translate/core'; import { Detail } from 'app/detail-overview-list/detail.model'; +import dayjs from 'dayjs/esm'; +import { SortService } from 'app/shared/service/sort.service'; @Component({ selector: 'jhi-tutorial-group-detail', @@ -27,7 +29,6 @@ export class TutorialGroupDetailComponent implements OnChanges { @Input() course: Course; formattedAdditionalInformation?: SafeHtml; - getDayTranslationKey = getDayTranslationKey; faQuestionCircle = faQuestionCircle; readonly Math = Math; @@ -40,6 +41,7 @@ export class TutorialGroupDetailComponent implements OnChanges { private artemisMarkdownService: ArtemisMarkdownService, private changeDetectorRef: ChangeDetectorRef, private translateService: TranslateService, + private sortService: SortService, ) {} ngOnChanges(changes: SimpleChanges) { @@ -137,4 +139,18 @@ export class TutorialGroupDetailComponent implements OnChanges { }, ]; } + + recalculateAttendanceDetails() { + let activeAndFinishedSessions = + this.tutorialGroup.tutorialGroupSessions?.filter((session) => session.status === TutorialGroupSessionStatus.ACTIVE && dayjs().isAfter(session.end)) ?? []; + activeAndFinishedSessions = this.sortService.sortByProperty(activeAndFinishedSessions, 'start', false); + const relevantSessions = activeAndFinishedSessions.slice(0, 3).filter((session) => session.attendanceCount !== undefined); + + if (relevantSessions.length) { + const averageAttendance = relevantSessions.map((session) => session.attendanceCount ?? 0).reduce((acc, attendance) => acc + attendance) / relevantSessions.length; + this.tutorialGroup.averageAttendance = Math.round(averageAttendance); + } + + this.getTutorialDetailSections(); + } } diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-group-row/tutorial-group-row.component.html b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-group-row/tutorial-group-row.component.html index 020fd8fd6087..db0fc31af7c3 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-group-row/tutorial-group-row.component.html +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-group-row/tutorial-group-row.component.html @@ -12,7 +12,7 @@ - {{ (tutorialGroup.capacity ?? '') + ' / ' + (tutorialGroup.numberOfRegisteredUsers ?? '') }} + {{ (tutorialGroup.numberOfRegisteredUsers ?? '') + ' / ' + (tutorialGroup.capacity ?? '') }} {{ tutorialGroup.isUserTutor ? ('global.generic.you' | artemisTranslate) : tutorialGroup.teachingAssistantName }} @if (tutorialGroup.tutorialGroupSchedule) { diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-groups-table.component.html b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-groups-table.component.html index dd11bbfd8b7b..04e87a32a23d 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-groups-table.component.html +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-groups-table.component.html @@ -22,7 +22,7 @@ - {{ 'artemisApp.entities.tutorialGroup.capacityWithRegistrations' | artemisTranslate }} + {{ 'artemisApp.entities.tutorialGroup.registrationsWithCapacity' | artemisTranslate }} diff --git a/src/main/webapp/app/exam/participate/summary/exercises/programming-exam-summary/programming-exam-summary.component.html b/src/main/webapp/app/exam/participate/summary/exercises/programming-exam-summary/programming-exam-summary.component.html index d314bf4dc71b..df611ba4e49a 100644 --- a/src/main/webapp/app/exam/participate/summary/exercises/programming-exam-summary/programming-exam-summary.component.html +++ b/src/main/webapp/app/exam/participate/summary/exercises/programming-exam-summary/programming-exam-summary.component.html @@ -49,7 +49,7 @@
      {{ 'artemisApp.exam.examSummary.assessment' | artemisTranslate }}
      {{ 'artemisApp.exam.examSummary.problemStatement' | artemisTranslate }}
      @if (exercise.problemStatement) { - + }
      diff --git a/src/main/webapp/app/localvc/repository-view/repository-view.component.html b/src/main/webapp/app/localvc/repository-view/repository-view.component.html index c559db8d4906..298e344e463c 100644 --- a/src/main/webapp/app/localvc/repository-view/repository-view.component.html +++ b/src/main/webapp/app/localvc/repository-view/repository-view.component.html @@ -18,11 +18,13 @@

      Source

      tutorialGroup.isUserRegistered); + if (this.course?.isAtLeastTutor) { + return this.tutorialGroups.filter((tutorialGroup) => tutorialGroup.isUserTutor); + } else { + return this.tutorialGroups.filter((tutorialGroup) => tutorialGroup.isUserRegistered); + } } ngOnInit(): void { diff --git a/src/main/webapp/i18n/de/tutorialGroups.json b/src/main/webapp/i18n/de/tutorialGroups.json index 815f641213ca..e1c4dfdccf98 100644 --- a/src/main/webapp/i18n/de/tutorialGroups.json +++ b/src/main/webapp/i18n/de/tutorialGroups.json @@ -40,7 +40,7 @@ "language": "Sprache", "teachingAssistant": "Tutor:in", "registrations": "Registrierungen", - "capacityWithRegistrations": "Kapazität / Registrierungen", + "registrationsWithCapacity": "Registrierungen / Kapazität", "schedule": "Sitzungsplan", "sessions": "Sitzungen", "nextSession": "Nächste Sitzung", diff --git a/src/main/webapp/i18n/en/tutorialGroups.json b/src/main/webapp/i18n/en/tutorialGroups.json index 427f450219fd..178ab7352d84 100644 --- a/src/main/webapp/i18n/en/tutorialGroups.json +++ b/src/main/webapp/i18n/en/tutorialGroups.json @@ -40,7 +40,7 @@ "language": "Language", "teachingAssistant": "Tutor", "registrations": "Registrations", - "capacityWithRegistrations": "Capacity / Registrations", + "registrationsWithCapacity": "Registrations / Capacity", "schedule": "Schedule", "sessions": "Sessions", "nextSession": "Next Session", diff --git a/src/test/java/de/tum/in/www1/artemis/exercise/programmingexercise/ProgrammingExerciseTestService.java b/src/test/java/de/tum/in/www1/artemis/exercise/programmingexercise/ProgrammingExerciseTestService.java index 70ba01e03c39..c11bc68abd65 100644 --- a/src/test/java/de/tum/in/www1/artemis/exercise/programmingexercise/ProgrammingExerciseTestService.java +++ b/src/test/java/de/tum/in/www1/artemis/exercise/programmingexercise/ProgrammingExerciseTestService.java @@ -701,7 +701,7 @@ void createProgrammingExercise_validExercise_withStaticCodeAnalysis(ProgrammingL var staticCodeAnalysisCategories = staticCodeAnalysisCategoryRepository.findByExerciseId(generatedExercise.getId()); assertThat(staticCodeAnalysisCategories).usingRecursiveFieldByFieldElementComparatorIgnoringFields("id", "exercise") .isEqualTo(StaticCodeAnalysisConfigurer.staticCodeAnalysisConfiguration().get(exercise.getProgrammingLanguage())); - StaticCodeAnalysisConfigurer.staticCodeAnalysisConfiguration().get(exercise.getProgrammingLanguage()).forEach(config -> config.getCategoryMappings().forEach(mapping -> { + StaticCodeAnalysisConfigurer.staticCodeAnalysisConfiguration().get(exercise.getProgrammingLanguage()).forEach(config -> config.categoryMappings().forEach(mapping -> { assertThat(mapping.tool()).isNotNull(); assertThat(mapping.category()).isNotNull(); })); diff --git a/src/test/java/de/tum/in/www1/artemis/util/IrisUtilTestService.java b/src/test/java/de/tum/in/www1/artemis/util/IrisUtilTestService.java index 57d5153dd333..43b6452dee41 100644 --- a/src/test/java/de/tum/in/www1/artemis/util/IrisUtilTestService.java +++ b/src/test/java/de/tum/in/www1/artemis/util/IrisUtilTestService.java @@ -146,16 +146,7 @@ public ProgrammingExercise setupTest(ProgrammingExercise exercise, LocalReposito doReturn(gitService.getExistingCheckedOutRepositoryByLocalPath(testRepo.localRepoFile.toPath(), null)).when(gitService).getOrCheckoutRepository(eq(testRepoUri), eq(false), any()); - var savedExercise = exerciseRepository.save(exercise); - // programmingExerciseUtilService.addSolutionParticipationForProgrammingExercise(savedExercise); - // var solutionParticipation = solutionProgrammingExerciseParticipationRepository.findByProgrammingExerciseId(savedExercise.getId()).orElseThrow(); - // solutionParticipation.setRepositoryUri(solutionRepoUri.toString()); - // solutionProgrammingExerciseParticipationRepository.save(solutionParticipation); - // var solutionSubmission = new ProgrammingSubmission(); - // solutionSubmission.setParticipation(solutionParticipation); - // programmingSubmissionRepository.save(solutionSubmission); - - return savedExercise; + return exerciseRepository.save(exercise); } } diff --git a/src/test/javascript/spec/component/tutorial-groups/course-tutorial-groups/course-tutorial-groups.component.spec.ts b/src/test/javascript/spec/component/tutorial-groups/course-tutorial-groups/course-tutorial-groups.component.spec.ts index 853d66568632..9b4638d33de1 100644 --- a/src/test/javascript/spec/component/tutorial-groups/course-tutorial-groups/course-tutorial-groups.component.spec.ts +++ b/src/test/javascript/spec/component/tutorial-groups/course-tutorial-groups/course-tutorial-groups.component.spec.ts @@ -48,8 +48,9 @@ describe('CourseTutorialGroupsComponent', () => { let fixture: ComponentFixture; let component: CourseTutorialGroupsComponent; - let tutorialGroupTwo: TutorialGroup; let tutorialGroupOne: TutorialGroup; + let tutorialGroupTwo: TutorialGroup; + let tutorialGroupThree: TutorialGroup; const router = new MockRouter(); @@ -88,8 +89,9 @@ describe('CourseTutorialGroupsComponent', () => { .then(() => { fixture = TestBed.createComponent(CourseTutorialGroupsComponent); component = fixture.componentInstance; - tutorialGroupOne = generateExampleTutorialGroup({ id: 1 }); - tutorialGroupTwo = generateExampleTutorialGroup({ id: 2 }); + tutorialGroupOne = generateExampleTutorialGroup({ id: 1, isUserTutor: true }); + tutorialGroupTwo = generateExampleTutorialGroup({ id: 2, isUserRegistered: true }); + tutorialGroupThree = generateExampleTutorialGroup({ id: 3 }); }); }); @@ -164,4 +166,16 @@ describe('CourseTutorialGroupsComponent', () => { replaceUrl: true, }); }); + + it('should filter registered tutorial groups for student', () => { + component.tutorialGroups = [tutorialGroupOne, tutorialGroupTwo, tutorialGroupThree]; + component.course = { id: 1, title: 'Test Course' } as Course; + expect(component.registeredTutorialGroups).toEqual([tutorialGroupTwo]); + }); + + it('should filter registered tutorial groups for tutor', () => { + component.tutorialGroups = [tutorialGroupOne, tutorialGroupTwo, tutorialGroupThree]; + component.course = { id: 1, title: 'Test Course', isAtLeastTutor: true } as Course; + expect(component.registeredTutorialGroups).toEqual([tutorialGroupOne]); + }); }); diff --git a/src/test/javascript/spec/component/tutorial-groups/shared/tutorial-group-detail.component.spec.ts b/src/test/javascript/spec/component/tutorial-groups/shared/tutorial-group-detail.component.spec.ts index f5b17e7c1a87..a997564fe086 100644 --- a/src/test/javascript/spec/component/tutorial-groups/shared/tutorial-group-detail.component.spec.ts +++ b/src/test/javascript/spec/component/tutorial-groups/shared/tutorial-group-detail.component.spec.ts @@ -18,6 +18,9 @@ import { TranslateService } from '@ngx-translate/core'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { MockLocalStorageService } from '../../../helpers/mocks/service/mock-local-storage.service'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; +import dayjs from 'dayjs/esm'; +import { TutorialGroupSessionStatus } from 'app/entities/tutorial-group/tutorial-group-session.model'; +import { ChangeDetectorRef } from '@angular/core'; @Component({ selector: 'jhi-mock-header', template: '
      ' }) class MockHeaderComponent { @@ -112,7 +115,7 @@ describe('TutorialGroupDetailComponent', () => { MockComponent(TutorialGroupUtilizationIndicatorComponent), MockPipe(RemoveSecondsPipe), ], - providers: [MockProvider(ArtemisMarkdownService), MockProvider(SortService)], + providers: [MockProvider(ArtemisMarkdownService), { provide: TranslateService, useClass: MockTranslateService }, MockProvider(ChangeDetectorRef)], }) .compileComponents() .then(() => { @@ -130,4 +133,45 @@ describe('TutorialGroupDetailComponent', () => { it('should initialize', () => { expect(component).not.toBeNull(); }); + + it.each([ + [ + { + tutorialGroupSessions: [ + { start: dayjs().subtract(1, 'minute'), end: dayjs().subtract(1, 'minute'), attendanceCount: 0, status: TutorialGroupSessionStatus.ACTIVE }, + ], + } as TutorialGroup, + 0, + ], + [ + { tutorialGroupSessions: [{ start: dayjs(), end: dayjs().add(1, 'minute'), attendanceCount: 42, status: TutorialGroupSessionStatus.ACTIVE }] } as TutorialGroup, + undefined, + ], + [ + { + tutorialGroupSessions: [ + { start: dayjs().subtract(3, 'hours'), end: dayjs().subtract(1, 'hour'), attendanceCount: 42, status: TutorialGroupSessionStatus.CANCELLED }, + ], + } as TutorialGroup, + undefined, + ], + [ + { + tutorialGroupSessions: [ + { start: dayjs().subtract(4, 'weeks'), end: dayjs().subtract(4, 'weeks'), attendanceCount: 48, status: TutorialGroupSessionStatus.ACTIVE }, + { start: dayjs().subtract(3, 'weeks'), end: dayjs().subtract(3, 'weeks'), attendanceCount: undefined, status: TutorialGroupSessionStatus.ACTIVE }, + { start: dayjs().subtract(2, 'weeks'), end: dayjs().subtract(2, 'weeks'), attendanceCount: 35, status: TutorialGroupSessionStatus.ACTIVE }, + { start: dayjs().subtract(1, 'weeks'), end: dayjs().subtract(1, 'weeks'), attendanceCount: 22, status: TutorialGroupSessionStatus.CANCELLED }, + { start: dayjs().subtract(1, 'day'), end: dayjs().subtract(1, 'day'), attendanceCount: 13, status: TutorialGroupSessionStatus.ACTIVE }, + { start: dayjs().add(1, 'weeks'), end: dayjs().add(1, 'weeks'), attendanceCount: 10, status: TutorialGroupSessionStatus.ACTIVE }, + { start: dayjs().add(2, 'weeks'), end: dayjs().add(2, 'weeks'), attendanceCount: undefined, status: TutorialGroupSessionStatus.ACTIVE }, + ], + } as TutorialGroup, + 24, + ], + ])('should calculate average attendance correctly', (tutorialGroup: TutorialGroup, expectedAttendance: number) => { + component.tutorialGroup = tutorialGroup; + component.recalculateAttendanceDetails(); + expect(component.tutorialGroup.averageAttendance).toBe(expectedAttendance); + }); }); diff --git a/supporting_scripts/extract_number_of_server_starts.sh b/supporting_scripts/extract_number_of_server_starts.sh index 3677527b6f20..80f07e848207 100644 --- a/supporting_scripts/extract_number_of_server_starts.sh +++ b/supporting_scripts/extract_number_of_server_starts.sh @@ -9,8 +9,8 @@ then exit 1 fi -if [[ $numberOfStarts -gt 5 ]] +if [[ $numberOfStarts -ne 4 ]] then - echo "The number of Server Starts should not be greater than 5!" + echo "The number of Server Starts should be equal to 4! Please adapt this check if the change is intended or try to fix the underlying issue causing a different number of server starts!" exit 1 fi