Skip to content

Commit

Permalink
fix: allow deletion of scheduled change requests (#3713)
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewelwell committed Apr 4, 2024
1 parent 6bebcef commit cd1f79c
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 2 deletions.
3 changes: 2 additions & 1 deletion api/features/versioning/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ def publish(
persist: bool = True,
) -> None:
now = timezone.now()
self.live_from = live_from or now

self.live_from = live_from or (self.live_from or now)
self.published_at = now
self.published_by = published_by
if persist:
Expand Down
23 changes: 22 additions & 1 deletion api/features/workflows/core/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import importlib
import logging
import typing
from datetime import datetime

from core.helpers import get_current_site_url
from core.models import (
Expand Down Expand Up @@ -239,11 +240,31 @@ def prevent_change_request_delete_if_committed(self) -> None:
# In the workflows-logic module, we prevent change requests from being
# deleted but, since this can have unexpected effects on published
# feature states, we also want to prevent it at the ORM level.
if self.committed_at and not self.environment.deleted_at:
if self.committed_at and not (
self.environment.deleted_at
or (self._live_from and self._live_from > timezone.now())
):
raise ChangeRequestDeletionError(
"Cannot delete a Change Request that has been committed."
)

@property
def _live_from(self) -> datetime | None:
# First we check if there are feature states associated with the change request
# and, if so, we return the live_from of the feature state with the earliest
# live_from.
if first_feature_state := self.feature_states.order_by("live_from").first():
return first_feature_state.live_from

# Then we do the same for environment feature versions. Note that a change request
# can not have feature states and environment feature versions.
elif first_environment_feature_version := self.environment_feature_versions.order_by(
"live_from"
).first():
return first_environment_feature_version.live_from

return None


class ChangeRequestApproval(LifecycleModel, abstract_base_auditable_model_factory()):
related_object_type = RelatedObjectType.CHANGE_REQUEST
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -626,3 +626,58 @@ def test_cannot_delete_committed_change_request(

# Then
# exception raised


def test_can_delete_committed_change_request_scheduled_for_the_future(
change_request: ChangeRequest,
admin_user: FFAdminUser,
feature: Feature,
environment: Environment,
) -> None:
# Given
FeatureState.objects.create(
feature=feature,
environment=environment,
change_request=change_request,
live_from=timezone.now() + timedelta(days=1),
version=None,
)

change_request.commit(admin_user)
change_request.save()

# When
change_request.delete()

# Then
assert not ChangeRequest.objects.filter(id=change_request.id).exists()


def test_can_delete_committed_change_request_scheduled_for_the_future_with_environment_feature_versions(
change_request: ChangeRequest,
admin_user: FFAdminUser,
feature: Feature,
environment: Environment,
) -> None:
# Given
environment_feature_version = EnvironmentFeatureVersion.objects.create(
feature=feature,
environment=environment,
live_from=timezone.now() + timedelta(days=1),
change_request=change_request,
)
FeatureState.objects.create(
feature=feature,
environment=environment,
environment_feature_version=environment_feature_version,
version=None,
)

change_request.commit(admin_user)
change_request.save()

# When
change_request.delete()

# Then
assert not ChangeRequest.objects.filter(id=change_request.id).exists()

0 comments on commit cd1f79c

Please sign in to comment.