Skip to content

Commit 3972e8d

Browse files
committed
feat: Added site deletion endpoint.
1 parent 3dbf26f commit 3972e8d

File tree

4 files changed

+132
-1
lines changed

4 files changed

+132
-1
lines changed

ecommerce/extensions/edly_ecommerce_app/api/v1/constants.py

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
'DJANGO_SETTINGS_UPDATE_FAILURE': _('Django settings update failed.'),
1414
'SITE_CONFIGURATIONS_UPDATE_SUCCESS': _('Site Configurations updated successfully.'),
1515
'SITE_CONFIGURATIONS_UPDATE_FAILURE': _('Site Configurations update failed.'),
16+
'SITE_DELETION_SUCCESS': _('Ecommerce site deletion was successful.'),
17+
'SITE_DELETION_FAILURE': _('Ecommerce site deletion failed.'),
1618
}
1719

1820
CLIENT_SITE_SETUP_FIELDS = [

ecommerce/extensions/edly_ecommerce_app/api/v1/urls.py

+1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88
url(r'site_themes/', views.SiteThemesActions.as_view(), name='site_themes'),
99
url(r'csrf_token/', views.CSRFTokenInfo.as_view(), name='get_csrf_token'),
1010
url(r'edly_sites/', views.EdlySiteViewSet.as_view(), name='edly_sites'),
11+
url(r'delete_site/', views.EdlySiteDeletionViewSet.as_view(), name='edly_delete_site'),
1112
url(r'edly_site_config/', views.EdlySiteConfigViewset.as_view(), name='edly_site_config'),
1213
]

ecommerce/extensions/edly_ecommerce_app/api/v1/views.py

+118-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from django.contrib.auth.decorators import login_required
66
from django.contrib.sites.models import Site
77
from django.contrib.sites.shortcuts import get_current_site
8+
from django.contrib.auth import get_user_model
9+
from django.db import transaction
810
from django.http import Http404
911
from django.middleware import csrf
1012
from django.utils.decorators import method_decorator
@@ -18,6 +20,7 @@
1820

1921
from ecommerce.core.constants import ENABLE_SUBSCRIPTIONS_ON_RUNTIME_SWITCH
2022
from ecommerce.core.models import SiteConfiguration
23+
from ecommerce.courses.models import Course
2124
from ecommerce.extensions.edly_ecommerce_app.api.v1.constants import ERROR_MESSAGES
2225
from ecommerce.extensions.edly_ecommerce_app.helpers import (
2326
user_is_course_creator,
@@ -27,9 +30,13 @@
2730
validate_site_configurations_for_self_service_api,
2831
validate_site_theme,
2932
)
30-
from ecommerce.extensions.edly_ecommerce_app.permissions import CanAccessSiteCreation
33+
from ecommerce.extensions.edly_ecommerce_app.permissions import CanAccessSiteCreation, CanAccessSiteDeletion
3134
from ecommerce.extensions.partner.models import Partner
3235
from ecommerce.theming.models import SiteTheme
36+
import logging
37+
38+
logger = logging.getLogger(__name__)
39+
User = get_user_model()
3340

3441

3542
class SiteThemesActions(APIView):
@@ -205,6 +212,116 @@ def get_oauth2_credentials(self):
205212
)
206213
return oauth2_values
207214

215+
class EdlySiteDeletionViewSet(APIView):
216+
"""
217+
Delete the ecommerce site and its data.
218+
"""
219+
permission_classes = [IsAuthenticated, CanAccessSiteDeletion]
220+
221+
def post(self, request):
222+
"""
223+
POST /edly_ecommerce_api/delete_site/
224+
"""
225+
try:
226+
with transaction.atomic():
227+
self.process_site_deletion(request)
228+
229+
return Response(
230+
{'success': ERROR_MESSAGES.get('SITE_DELETION_SUCCESS')},
231+
status=status.HTTP_200_OK
232+
)
233+
except Exception as e:
234+
logger.error(f"Site deletion failed: {str(e)}", exc_info=True)
235+
return Response(
236+
{
237+
'error': ERROR_MESSAGES.get('SITE_DELETION_FAILURE'),
238+
'detail': str(e)
239+
},
240+
status=status.HTTP_400_BAD_REQUEST
241+
)
242+
243+
def delete_users(self, request):
244+
"""Delete all the sync user for a given site."""
245+
user_emails = request.data.get('emails')
246+
user_names = request.data.get('usernames')
247+
if not all([len(user_emails), len(user_names)]):
248+
logger.info(f"No user deleted for given site : {(str(request.site))}")
249+
return
250+
251+
users = User.objects.filter(
252+
email__in=user_emails,
253+
username__in=user_names
254+
).exclude(id=request.user.id)
255+
from ecommerce.extensions.order.models import MarkOrdersStatusCompleteConfig
256+
MarkOrdersStatusCompleteConfig.objects.filter(changed_by__in=users).delete()
257+
users.delete()
258+
logger.info(f"Successfully deleted user for {request.site}")
259+
260+
def delete_courses(self, request):
261+
"""Delete the courses for the site."""
262+
partner = request.site.partner
263+
courses = Course.objects.filter(partner=partner)
264+
265+
course_ids = courses.values_list('id', flat=True)
266+
267+
HistoricalCourse = Course.history.model
268+
HistoricalCourse.objects.filter(id__in=course_ids).delete()
269+
courses.delete()
270+
271+
def delete_partner_additional_data(self, request):
272+
"""
273+
Delete all related data referencing the partner that is not automatically
274+
deleted due to on_delete settings.
275+
"""
276+
from ecommerce.extensions.partner.models import StockRecord
277+
from ecommerce.extensions.order.models import Order, Line
278+
from ecommerce.extensions.offer.models import ConditionalOffer
279+
280+
site = request.site
281+
partner = Partner.objects.get(default_site=site)
282+
283+
HistoricalConditionalOffer = ConditionalOffer.history.model
284+
count, _ = HistoricalConditionalOffer.objects.filter(partner=partner).delete()
285+
logger.info(f"Deleted {count} HistoricalConditionalOffer record(s) for partner {partner}")
286+
287+
HistoricalOrder = Order.history.model
288+
count, _ = HistoricalOrder.objects.filter(partner=partner).delete()
289+
logger.info(f"Deleted {count} HistoricalOrder record(s) for partner {partner}")
290+
291+
count, _ = Line.objects.filter(partner=partner).delete()
292+
logger.info(f"Deleted {count} Line record(s) for partner {partner}")
293+
HistoricalLine = Line.history.model
294+
count, _ = HistoricalLine.objects.filter(partner=partner).delete()
295+
logger.info(f"Deleted {count} HistoricalLine record(s) for partner {partner}")
296+
297+
HistoricalStockRecord = StockRecord.history.model
298+
count, _ = HistoricalStockRecord.objects.filter(partner=partner).delete()
299+
logger.info(f"Deleted {count} HistoricalStockRecord record(s) for partner {partner}")
300+
301+
partner.history.all().delete()
302+
303+
304+
def delete_site(self, request):
305+
"""Delete the site and partner for a given site."""
306+
site = request.site
307+
try:
308+
site_partner = Partner.objects.get(default_site=site)
309+
site_partner.delete()
310+
site.delete()
311+
logger.info(f"Successfully deleted site {site} and its partner")
312+
except Partner.DoesNotExist:
313+
logger.info(f'Partner Not Found for site {request.site}')
314+
315+
def process_site_deletion(self, request):
316+
"""
317+
Process deletion of a site.
318+
"""
319+
self.delete_courses(request)
320+
self.delete_partner_additional_data(request)
321+
self.delete_users(request)
322+
self.delete_site(request)
323+
324+
208325

209326
class EdlySiteConfigViewset(APIView):
210327
"""

ecommerce/extensions/edly_ecommerce_app/permissions.py

+11
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,14 @@ def has_permission(self, request, view):
2424
Checks for user's permission for current site.
2525
"""
2626
return request.user.is_staff and request.user.username == EDLY_PANEL_WORKER_USER
27+
28+
class CanAccessSiteDeletion(BasePermission):
29+
"""
30+
Checks if a user has the access to create and update methods for sites.
31+
"""
32+
33+
def has_permission(self, request, view):
34+
"""
35+
Checks for user's permission for current site.
36+
"""
37+
return request.user.username == EDLY_PANEL_WORKER_USER

0 commit comments

Comments
 (0)