diff --git a/CHANGELOG.md b/CHANGELOG.md index ad2f9a3515..860877abe7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed + +- Manage playlist constraint with DRF 3.15 + ## [5.0.0] - 2024-06-24 ### Added diff --git a/src/backend/marsha/core/migrations/0080_alter_playlist_lti_id.py b/src/backend/marsha/core/migrations/0080_alter_playlist_lti_id.py new file mode 100644 index 0000000000..b00272836f --- /dev/null +++ b/src/backend/marsha/core/migrations/0080_alter_playlist_lti_id.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.13 on 2024-07-03 14:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0079_timedtexttrack_process_pipeline"), + ] + + operations = [ + migrations.AlterField( + model_name="playlist", + name="lti_id", + field=models.CharField( + blank=True, + default=None, + help_text="ID for synchronization with an external LTI tool", + max_length=255, + null=True, + verbose_name="lti id", + ), + ), + ] diff --git a/src/backend/marsha/core/models/playlist.py b/src/backend/marsha/core/models/playlist.py index b94114c7e4..76e5f7236d 100644 --- a/src/backend/marsha/core/models/playlist.py +++ b/src/backend/marsha/core/models/playlist.py @@ -70,6 +70,7 @@ class Playlist(BaseModel): help_text=_("ID for synchronization with an external LTI tool"), null=True, blank=True, + default=None, ) organization = models.ForeignKey( to="Organization", diff --git a/src/backend/marsha/core/serializers/playlist.py b/src/backend/marsha/core/serializers/playlist.py index 35f60f199d..b13d7731a2 100644 --- a/src/backend/marsha/core/serializers/playlist.py +++ b/src/backend/marsha/core/serializers/playlist.py @@ -66,7 +66,6 @@ class Meta: # Field consuming the `can_edit` property filled by # the `PlaylistManager` `annotate_can_edit` can_edit = serializers.BooleanField(read_only=True) - lti_id = serializers.CharField(required=False) def get_portable_to(self, obj): """Getter for portable_to attribute instead of PlaylistSerializer to prevent recursion.""" diff --git a/src/backend/marsha/core/tests/api/playlists/test_create.py b/src/backend/marsha/core/tests/api/playlists/test_create.py index 6f2cb2cc35..30b4a8d109 100644 --- a/src/backend/marsha/core/tests/api/playlists/test_create.py +++ b/src/backend/marsha/core/tests/api/playlists/test_create.py @@ -201,3 +201,59 @@ def test_create_playlist_by_organization_administrator(self): self.assertEqual(created_permission.user, user) self.assertEqual(created_permission.playlist, created_playlist) self.assertEqual(created_permission.role, ADMINISTRATOR) + + def test_create_playlist_by_organization_administrator_without_lti_id(self): + """Organization administrators can create playlists without lti_id.""" + user = factories.UserFactory() + org = factories.OrganizationFactory() + factories.OrganizationAccessFactory( + role=models.ADMINISTRATOR, organization=org, user=user + ) + + jwt_token = UserAccessTokenFactory(user=user) + + self.assertEqual(models.Playlist.objects.count(), 0) + + response = self.client.post( + "/api/playlists/", + { + "consumer_site": "", + "organization": str(org.id), + "title": "Some playlist", + }, + HTTP_AUTHORIZATION=f"Bearer {jwt_token}", + ) + self.assertEqual(models.Playlist.objects.count(), 1) + + self.assertEqual(response.status_code, 201) + created_playlist = models.Playlist.objects.first() + self.assertEqual( + response.json(), + { + "consumer_site": None, + "created_by": str(user.id), + "created_on": created_playlist.created_on.isoformat().replace( + "+00:00", "Z" + ), + "duplicated_from": None, + "id": str(created_playlist.id), + "is_portable_to_playlist": False, + "is_portable_to_consumer_site": False, + "is_public": False, + "is_claimable": False, + "lti_id": None, + "organization": { + "id": str(org.id), + "name": org.name, + }, + "portable_to": [], + "retention_duration": None, + "title": "Some playlist", + "users": [str(user.id)], + }, + ) + + created_permission = models.PlaylistAccess.objects.first() + self.assertEqual(created_permission.user, user) + self.assertEqual(created_permission.playlist, created_playlist) + self.assertEqual(created_permission.role, ADMINISTRATOR) diff --git a/src/backend/marsha/core/tests/api/playlists/test_update.py b/src/backend/marsha/core/tests/api/playlists/test_update.py index 987ea7c6f0..3d18c0164f 100644 --- a/src/backend/marsha/core/tests/api/playlists/test_update.py +++ b/src/backend/marsha/core/tests/api/playlists/test_update.py @@ -49,7 +49,10 @@ def test_update_playlist_by_random_logged_in_user(self): def test_update_playlist_by_playlist_admin(self): """Playlist administrators can update playlists.""" user = factories.UserFactory() - playlist = factories.PlaylistFactory(title="existing title") + org_1 = factories.OrganizationFactory() + playlist = factories.PlaylistFactory( + title="existing title", organization=org_1, lti_id=None, consumer_site=None + ) factories.PlaylistAccessFactory( user=user, playlist=playlist, role=models.ADMINISTRATOR ) @@ -70,7 +73,13 @@ def test_update_playlist_by_playlist_admin(self): def test_update_playlist_by_playlist_instructor(self): """Playlist instructors can update playlists.""" user = factories.UserFactory() - playlist = factories.PlaylistFactory(title="title for instructor") + org_1 = factories.OrganizationFactory() + playlist = factories.PlaylistFactory( + title="title for instructor", + organization=org_1, + lti_id=None, + consumer_site=None, + ) factories.PlaylistAccessFactory( user=user, playlist=playlist, role=models.INSTRUCTOR ) @@ -96,7 +105,10 @@ def test_update_playlist_by_orga_administrator(self): user=user, organization=org_1, role=models.ADMINISTRATOR ) playlist = factories.PlaylistFactory( - title="title for orga admin", organization=org_1 + title="title for orga admin", + organization=org_1, + lti_id=None, + consumer_site=None, ) jwt_token = UserAccessTokenFactory(user=user)