appeler wcs pour déclarer une app ou supprimer les liens d'une app (#74659) #20
|
@ -71,6 +71,10 @@ class DeploymentError(ApplicationError):
|
|||
pass
|
||||
|
||||
|
||||
class UnlinkError(ApplicationError):
|
||||
pass
|
||||
|
||||
|
||||
class Application(models.Model):
|
||||
SUPPORTED_MODULES = ('wcs',)
|
||||
|
||||
|
@ -180,6 +184,24 @@ class Application(models.Model):
|
|||
elements[(element.type, element.slug)] = (element, relation)
|
||||
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):
|
||||
type = models.CharField(max_length=100, verbose_name=_('Type'))
|
||||
|
@ -241,7 +263,7 @@ class Version(models.Model):
|
|||
def __repr__(self):
|
||||
return '<Version %s>' % self.application.slug
|
||||
|
||||
def create_bundle(self):
|
||||
def create_bundle(self, job=None):
|
||||
app = self.application
|
||||
elements = app.scandeps()
|
||||
tar_io = io.BytesIO()
|
||||
|
@ -289,9 +311,23 @@ class Version(models.Model):
|
|||
self.bundle.save('%s.tar' % app.slug, content=ContentFile(tar_io.getvalue()))
|
||||
self.save()
|
||||
|
||||
bundle_content = self.bundle.read()
|
||||
self.do_something_with_bundle(bundle_content, 'declare', job=job)
|
||||
|
||||
def deploy(self, job=None):
|
||||
bundle_content = self.bundle.read()
|
||||
self.deploy_roles(bundle_content)
|
||||
self.do_something_with_bundle(bundle_content, 'deploy', job=job)
|
||||
self.application.refresh_elements(cache_only=True)
|
||||
|
||||
def do_something_with_bundle(self, bundle_content, action, job=None):
|
||||
if action == 'deploy':
|
||||
target_url = 'api/export-import/bundle-import/'
|
||||
exception_message = _('Failed to deploy module %s (%s)')
|
||||
elif action == 'declare':
|
||||
target_url = 'api/export-import/bundle-declare/'
|
||||
exception_message = _('Failed to declare elements for module %s (%s)')
|
||||
|
||||
for service_id, services in getattr(settings, 'KNOWN_SERVICES', {}).items():
|
||||
if service_id not in Application.SUPPORTED_MODULES:
|
||||
continue
|
||||
|
@ -301,12 +337,10 @@ class Version(models.Model):
|
|||
continue
|
||||
if service_objects[service['url']].secondary:
|
||||
continue
|
||||
url = urllib.parse.urljoin(service['url'], 'api/export-import/bundle-import/')
|
||||
url = urllib.parse.urljoin(service['url'], target_url)
|
||||
response = requests.put(url, data=bundle_content)
|
||||
if not response.ok:
|
||||
raise DeploymentError(
|
||||
_('Failed to deploy module %s (%s)') % (service_id, response.status_code)
|
||||
)
|
||||
raise DeploymentError(exception_message % (service_id, response.status_code))
|
||||
if not job:
|
||||
continue
|
||||
try:
|
||||
|
@ -319,7 +353,6 @@ class Version(models.Model):
|
|||
job.progression_urls[service_id] = {}
|
||||
job.progression_urls[service_id][service['title']] = response_json['url']
|
||||
job.save()
|
||||
self.application.refresh_elements(cache_only=True)
|
||||
|
||||
def get_authentic_service(self):
|
||||
for service_id, services in getattr(settings, 'KNOWN_SERVICES', {}).items():
|
||||
|
@ -396,7 +429,7 @@ class AsyncJob(models.Model):
|
|||
if self.action == 'scandeps':
|
||||
self.application.scandeps()
|
||||
elif self.action == 'create_bundle':
|
||||
self.version.create_bundle()
|
||||
self.version.create_bundle(self)
|
||||
elif self.action == 'deploy':
|
||||
self.version.deploy(self)
|
||||
except ApplicationError as e:
|
||||
|
|
|
@ -19,6 +19,7 @@ import io
|
|||
import json
|
||||
import tarfile
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db.models import Prefetch
|
||||
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 .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
|
||||
|
||||
requests = Requests()
|
||||
|
@ -383,6 +393,19 @@ class AppDeleteView(DeleteView):
|
|||
model = Application
|
||||
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):
|
||||
return reverse('applications-home')
|
||||
|
||||
|
|
|
@ -386,14 +386,28 @@ def test_create_application(app, admin_user, settings, analyze):
|
|||
assert versions.count('2.0') == 1
|
||||
assert resp.text.count('Creating application bundle') == 4
|
||||
|
||||
# non editable app
|
||||
application.editable = False
|
||||
application.save()
|
||||
app.get('/applications/manifest/test/metadata/', status=404)
|
||||
app.get('/applications/manifest/test/scandeps/', status=404)
|
||||
app.get('/applications/manifest/test/generate/', status=404)
|
||||
app.get('/applications/manifest/test/add/forms/', status=404)
|
||||
app.get('/applications/manifest/test/delete/%s/' % application.relation_set.first().pk, status=404)
|
||||
def response_content(url, request):
|
||||
if url.path == '/api/export-import/bundle-declare/':
|
||||
return {'status_code': 500}
|
||||
return mocked_http(url, request)
|
||||
|
||||
resp = app.get('/applications/manifest/test/generate/')
|
||||
with HTTMock(response_content):
|
||||
with pytest.raises(DeploymentError) as e:
|
||||
resp.form.submit()
|
||||
assert str(e.value) == 'Failed to declare elements for module wcs (500)'
|
||||
job = AsyncJob.objects.latest('pk')
|
||||
assert job.status == 'failed'
|
||||
assert job.exception == 'Failed to declare elements for module wcs (500)'
|
||||
|
||||
# non editable app
|
||||
application.editable = False
|
||||
application.save()
|
||||
app.get('/applications/manifest/test/metadata/', status=404)
|
||||
app.get('/applications/manifest/test/scandeps/', status=404)
|
||||
app.get('/applications/manifest/test/generate/', status=404)
|
||||
app.get('/applications/manifest/test/add/forms/', status=404)
|
||||
app.get('/applications/manifest/test/delete/%s/' % application.relation_set.first().pk, status=404)
|
||||
|
||||
|
||||
def test_manifest_ordering(app, admin_user, settings):
|
||||
|
@ -740,9 +754,8 @@ def test_delete_application(app, admin_user, settings):
|
|||
|
||||
with HTTMock(mocked_http):
|
||||
resp = app.get('/applications/manifest/app_to_delete/')
|
||||
resp = resp.click(re.compile('^Delete$'))
|
||||
|
||||
resp = resp.forms[0].submit()
|
||||
resp = resp.click(re.compile('^Delete$'))
|
||||
resp = resp.forms[0].submit()
|
||||
resp = resp.follow()
|
||||
|
||||
assert '/applications/' in resp
|
||||
|
@ -751,6 +764,20 @@ def test_delete_application(app, admin_user, settings):
|
|||
assert Application.objects.count() == 1
|
||||
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):
|
||||
login(app)
|
||||
|
|
Loading…
Reference in New Issue