applications: unlink app in services on deletion (#74659)
gitea/hobo/pipeline/head This commit looks good Details

This commit is contained in:
Lauréline Guérin 2023-02-20 16:34:04 +01:00
parent 3bb0a0c3ca
commit a1e8c69eef
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
3 changed files with 62 additions and 4 deletions

View File

@ -71,6 +71,10 @@ class DeploymentError(ApplicationError):
pass pass
class UnlinkError(ApplicationError):
pass
class Application(models.Model): class Application(models.Model):
SUPPORTED_MODULES = ('wcs',) SUPPORTED_MODULES = ('wcs',)
@ -180,6 +184,24 @@ class Application(models.Model):
elements[(element.type, element.slug)] = (element, relation) elements[(element.type, element.slug)] = (element, relation)
return elements return elements
def unlink(self):
for service_id, services in getattr(settings, 'KNOWN_SERVICES', {}).items():
if service_id not in Application.SUPPORTED_MODULES:
continue
service_objects = {x.get_base_url_path(): x for x in get_installed_services(types=[service_id])}
for service in services.values():
if service['url'] not in service_objects:
continue
if service_objects[service['url']].secondary:
continue
url = urllib.parse.urljoin(service['url'], 'api/export-import/unlink/')
response = requests.post(url, data={'application': self.slug})
if not response.ok:
raise UnlinkError(
_('Failed to unlink application in module %s (%s)')
% (service_id, response.status_code)
)
class Element(models.Model): class Element(models.Model):
type = models.CharField(max_length=100, verbose_name=_('Type')) type = models.CharField(max_length=100, verbose_name=_('Type'))

View File

@ -19,6 +19,7 @@ import io
import json import json
import tarfile import tarfile
from django.contrib import messages
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.db.models import Prefetch from django.db.models import Prefetch
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
@ -31,7 +32,16 @@ from django.views.generic import DetailView, FormView, ListView, TemplateView
from django.views.generic.edit import CreateView, DeleteView, UpdateView from django.views.generic.edit import CreateView, DeleteView, UpdateView
from .forms import GenerateForm, InstallForm, MetadataForm from .forms import GenerateForm, InstallForm, MetadataForm
from .models import STATUS_CHOICES, Application, AsyncJob, Element, Relation, Version, get_object_types from .models import (
STATUS_CHOICES,
Application,
AsyncJob,
Element,
Relation,
UnlinkError,
Version,
get_object_types,
)
from .utils import Requests from .utils import Requests
requests = Requests() requests = Requests()
@ -383,6 +393,19 @@ class AppDeleteView(DeleteView):
model = Application model = Application
template_name = 'hobo/applications/app_confirm_delete.html' template_name = 'hobo/applications/app_confirm_delete.html'
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
success_url = self.get_success_url()
try:
self.object.unlink()
except UnlinkError as e:
messages.error(self.request, str(e))
return HttpResponseRedirect(
reverse('application-manifest', kwargs={'app_slug': self.kwargs['slug']})
)
self.object.delete()
return HttpResponseRedirect(success_url)
def get_success_url(self): def get_success_url(self):
return reverse('applications-home') return reverse('applications-home')

View File

@ -754,9 +754,8 @@ def test_delete_application(app, admin_user, settings):
with HTTMock(mocked_http): with HTTMock(mocked_http):
resp = app.get('/applications/manifest/app_to_delete/') resp = app.get('/applications/manifest/app_to_delete/')
resp = resp.click(re.compile('^Delete$')) resp = resp.click(re.compile('^Delete$'))
resp = resp.forms[0].submit()
resp = resp.forms[0].submit()
resp = resp.follow() resp = resp.follow()
assert '/applications/' in resp assert '/applications/' in resp
@ -765,6 +764,20 @@ def test_delete_application(app, admin_user, settings):
assert Application.objects.count() == 1 assert Application.objects.count() == 1
assert Application.objects.first().name == 'OtherApp' assert Application.objects.first().name == 'OtherApp'
def response_content(url, request):
if url.path == '/api/export-import/unlink/':
return {'status_code': 500}
return mocked_http(url, request)
with HTTMock(response_content):
resp = app.get('/applications/manifest/other_app/')
resp = resp.click(re.compile('^Delete$'))
resp = resp.forms[0].submit()
resp = resp.follow()
assert 'Failed to unlink application in module wcs (500)' in resp
assert Application.objects.count() == 1
assert Application.objects.first().name == 'OtherApp'
def test_404_unknown_app(app, admin_user, settings): def test_404_unknown_app(app, admin_user, settings):
login(app) login(app)