diff --git a/api/features/feature_external_resources/migrations/0002_featureexternalresource_feature_ext_type_2b2068_idx.py b/api/features/feature_external_resources/migrations/0002_featureexternalresource_feature_ext_type_2b2068_idx.py new file mode 100644 index 000000000000..50b22105f1c1 --- /dev/null +++ b/api/features/feature_external_resources/migrations/0002_featureexternalresource_feature_ext_type_2b2068_idx.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.25 on 2024-04-24 18:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('feature_external_resources', '0001_initial'), + ] + + operations = [ + migrations.AddIndex( + model_name='featureexternalresource', + index=models.Index(fields=['type'], name='feature_ext_type_2b2068_idx'), + ), + ] diff --git a/api/features/feature_external_resources/models.py b/api/features/feature_external_resources/models.py index 79f62f2734c2..ccb075caa194 100644 --- a/api/features/feature_external_resources/models.py +++ b/api/features/feature_external_resources/models.py @@ -42,6 +42,9 @@ class Meta: fields=["feature", "url"], name="unique_feature_url_constraint" ) ] + indexes = [ + models.Index(fields=["type"]), + ] @hook(AFTER_SAVE) def exectute_after_save_actions(self): diff --git a/api/integrations/github/models.py b/api/integrations/github/models.py index e0c08340151a..7aebde9ce0bc 100644 --- a/api/integrations/github/models.py +++ b/api/integrations/github/models.py @@ -2,6 +2,7 @@ from core.models import SoftDeleteExportableModel from django.db import models +from django_lifecycle import BEFORE_DELETE, LifecycleModelMixin, hook from organisations.models import Organisation @@ -21,7 +22,7 @@ def has_github_configuration(organisation_id: int) -> bool: ).exists() -class GithubRepository(SoftDeleteExportableModel): +class GithubRepository(LifecycleModelMixin, SoftDeleteExportableModel): github_configuration = models.ForeignKey( GithubConfiguration, related_name="repository_config", on_delete=models.CASCADE ) @@ -47,3 +48,19 @@ class Meta: name="unique_repository_data", ) ] + + @hook(BEFORE_DELETE) + def delete_feature_external_resources( + self, + ) -> None: + from features.feature_external_resources.models import ( + FeatureExternalResource, + ) + + FeatureExternalResource.objects.filter( + feature_id__in=self.project.features.values_list("id", flat=True), + type__in=[ + FeatureExternalResource.ResourceType.GITHUB_ISSUE, + FeatureExternalResource.ResourceType.GITHUB_PR, + ], + ).delete() diff --git a/api/tests/unit/integrations/github/test_unit_github_views.py b/api/tests/unit/integrations/github/test_unit_github_views.py index ff64fa43e574..5e3fcecfcf58 100644 --- a/api/tests/unit/integrations/github/test_unit_github_views.py +++ b/api/tests/unit/integrations/github/test_unit_github_views.py @@ -1,9 +1,11 @@ import pytest from django.urls import reverse from pytest_lazyfixture import lazy_fixture +from pytest_mock import MockerFixture from rest_framework import status from rest_framework.test import APIClient +from features.feature_external_resources.models import FeatureExternalResource from integrations.github.models import GithubConfiguration, GithubRepository from organisations.models import Organisation from projects.models import Project @@ -191,18 +193,28 @@ def test_cannot_create_github_repository_due_to_unique_constraint( def test_github_delete_repository( client: APIClient, organisation: Organisation, + feature_external_resource: FeatureExternalResource, github_configuration: GithubConfiguration, github_repository: GithubRepository, + mocker: MockerFixture, ) -> None: # Given + mock_generate_token = mocker.patch( + "integrations.github.github.generate_token", + ) + mock_generate_token.return_value = "mocked_token" url = reverse( "api-v1:organisations:repositories-detail", args=[organisation.id, github_configuration.id, github_repository.id], ) + for feature in github_repository.project.features.all(): + assert FeatureExternalResource.objects.filter(feature=feature).exists() # When response = client.delete(url) # Then assert response.status_code == status.HTTP_204_NO_CONTENT + for feature in github_repository.project.features.all(): + assert not FeatureExternalResource.objects.filter(feature=feature).exists() def mocked_requests_get(*args, **kwargs):