diff --git a/hobo/applications/urls.py b/hobo/applications/urls.py index 09c38d5..052dd53 100644 --- a/hobo/applications/urls.py +++ b/hobo/applications/urls.py @@ -25,6 +25,7 @@ urlpatterns = [ re_path(r'^manifest/(?P[\w-]+)/delete/$', views.delete, name='application-delete'), re_path(r'^manifest/(?P[\w-]+)/$', views.manifest, name='application-manifest'), re_path(r'^manifest/(?P[\w-]+)/update/$', views.update, name='application-update'), + re_path(r'^manifest/(?P[\w-]+)/refresh/$', views.refresh, name='application-refresh'), re_path(r'^manifest/(?P[\w-]+)/versions/$', views.versions, name='application-versions'), re_path(r'^manifest/(?P[\w-]+)/metadata/$', views.metadata, name='application-metadata'), re_path(r'^manifest/(?P[\w-]+)/scandeps/$', views.scandeps, name='application-scandeps'), diff --git a/hobo/applications/views.py b/hobo/applications/views.py index 3d0963a..8342f63 100644 --- a/hobo/applications/views.py +++ b/hobo/applications/views.py @@ -28,7 +28,7 @@ from django.urls import reverse, reverse_lazy from django.utils.text import slugify from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ -from django.views.generic import DetailView, FormView, ListView, TemplateView +from django.views.generic import DetailView, FormView, ListView, RedirectView, TemplateView from django.views.generic.edit import CreateView, DeleteView, UpdateView from hobo.environment.models import Variable @@ -37,6 +37,7 @@ from .forms import GenerateForm, InstallForm, MetadataForm from .models import ( STATUS_CHOICES, Application, + ApplicationError, AsyncJob, Element, Parameter, @@ -392,6 +393,19 @@ class Update(Install): update = Update.as_view() +class Refresh(RedirectView): + def get_redirect_url(self, *args, **kwargs): + application = get_object_or_404(Application, slug=kwargs['app_slug']) + try: + application.refresh_elements(cache_only=True) + except ApplicationError: + pass + return reverse('application-manifest', kwargs={'app_slug': self.kwargs['app_slug']}) + + +refresh = Refresh.as_view() + + class AppDeleteView(DeleteView): model = Application template_name = 'hobo/applications/app_confirm_delete.html' @@ -450,6 +464,8 @@ class AsyncJobView(DetailView): return super().get_context_data(**kwargs) def get_redirect_url(self): + if self.object.action == 'deploy': + return reverse('application-refresh', kwargs={'app_slug': self.kwargs['app_slug']}) return reverse('application-manifest', kwargs={'app_slug': self.kwargs['app_slug']}) diff --git a/tests/test_application.py b/tests/test_application.py index b4f9fec..76d2b52 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -1023,6 +1023,67 @@ def test_update_application(app, admin_user, settings, app_bundle): assert resp.context['form'].errors == {'bundle': ['Can not update this application, wrong slug (test).']} +def test_refresh_application(app, admin_user, settings): + Wcs.objects.create(base_url='https://wcs.example.invalid', slug='foobar', title='Foobar') + + settings.KNOWN_SERVICES = { + 'wcs': { + 'foobar': { + 'title': 'Foobar', + 'url': 'https://wcs.example.invalid/', + 'orig': 'example.org', + 'secret': 'xxx', + } + } + } + + login(app) + + application = Application.objects.create(name='Test', slug='test') + element = Element.objects.create(type='forms', slug='test', name='Test', cache={}) + Relation.objects.create(application=application, element=element) + + form_def = { + "id": "test", + "text": "Test", + "type": "forms", + "urls": { + "export": "https://wcs.example.invalid/api/export-import/forms/test/", + "dependencies": "https://wcs.example.invalid/api/export-import/forms/test/dependencies/", + "redirect": "https://wcs.example.invalid/api/export-import/forms/test/redirect/", + }, + } + + def response_content(url, request): + if url.path == '/api/export-import/forms/': + return { + 'content': {"data": [form_def]}, + 'status_code': 200, + } + return mocked_http(url, request) + + with HTTMock(response_content): + resp = app.get('/applications/manifest/test/refresh/') + application = Application.objects.get(slug='test') + element.refresh_from_db() + assert element.cache == form_def + assert resp.location.endswith('/applications/manifest/test/') + + def response_content(url, request): + if url.path == '/api/export-import/forms/': + return {'status_code': 500} + return mocked_http(url, request) + + element.cache = {'foo': 'bar'} + element.save() + with HTTMock(response_content): + resp = app.get('/applications/manifest/test/refresh/') + application = Application.objects.get(slug='test') + element.refresh_from_db() + assert element.cache == {'foo': 'bar'} + assert resp.location.endswith('/applications/manifest/test/') + + @pytest.fixture def app_bundle_roles(): tar_io = io.BytesIO() @@ -1222,7 +1283,7 @@ def test_job_status_page(app, admin_user, settings): assert 'Please wait…' in resp assert 'Completed: 100%' in resp assert 'window.location.reload' not in resp - assert 'window.location = "/applications/manifest/test/"' in resp + assert 'window.location = "/applications/manifest/test/refresh/"' in resp job.status = 'failed' job.exception = 'foo bar exception'