applications: add possibility to define parameters (#76463)
This commit is contained in:
parent
5f0177d901
commit
aee3fb8093
|
@ -0,0 +1,41 @@
|
||||||
|
# Generated by Django 3.2.16 on 2023-04-10 15:53
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('applications', '0012_visible'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Parameter',
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
'id',
|
||||||
|
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||||
|
),
|
||||||
|
('label', models.CharField(max_length=100, verbose_name='Label')),
|
||||||
|
(
|
||||||
|
'name',
|
||||||
|
models.CharField(
|
||||||
|
help_text='Variable name, it is useful to prefix it with an unique application identifier.',
|
||||||
|
max_length=100,
|
||||||
|
verbose_name='Identifier',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'default_value',
|
||||||
|
models.CharField(blank=True, max_length=100, null=True, verbose_name='Default value'),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'application',
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE, to='applications.application'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
|
@ -30,6 +30,7 @@ from django.utils.text import slugify
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from hobo.environment.models import Variable
|
||||||
from hobo.environment.utils import get_installed_services
|
from hobo.environment.utils import get_installed_services
|
||||||
|
|
||||||
from .utils import Requests
|
from .utils import Requests
|
||||||
|
@ -206,6 +207,27 @@ class Application(models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Parameter(models.Model):
|
||||||
|
application = models.ForeignKey(Application, on_delete=models.CASCADE)
|
||||||
|
label = models.CharField(max_length=100, verbose_name=_('Label'))
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=100,
|
||||||
|
verbose_name=_('Identifier'),
|
||||||
|
help_text=_('Variable name, it is useful to prefix it with an unique application identifier.'),
|
||||||
|
)
|
||||||
|
default_value = models.CharField(max_length=100, verbose_name=_('Default value'), blank=True, null=True)
|
||||||
|
|
||||||
|
def as_dict(self):
|
||||||
|
return {'label': self.label, 'name': self.name, 'default_value': self.default_value}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def variable(self):
|
||||||
|
variable, _ = Variable.objects.get_or_create(
|
||||||
|
name=self.name, defaults={'value': self.default_value or '', 'auto': True, 'label': self.label}
|
||||||
|
)
|
||||||
|
return variable
|
||||||
|
|
||||||
|
|
||||||
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'))
|
||||||
slug = models.SlugField(max_length=500, verbose_name=_('Slug'))
|
slug = models.SlugField(max_length=500, verbose_name=_('Slug'))
|
||||||
|
@ -281,6 +303,7 @@ class Version(models.Model):
|
||||||
'version_number': self.number,
|
'version_number': self.number,
|
||||||
'version_notes': self.notes,
|
'version_notes': self.notes,
|
||||||
'elements': [],
|
'elements': [],
|
||||||
|
'parameters': [x.as_dict() for x in self.application.parameter_set.all()],
|
||||||
}
|
}
|
||||||
|
|
||||||
for element, relation in elements.values():
|
for element, relation in elements.values():
|
||||||
|
@ -320,6 +343,7 @@ class Version(models.Model):
|
||||||
|
|
||||||
def deploy(self, job=None):
|
def deploy(self, job=None):
|
||||||
bundle_content = self.bundle.read()
|
bundle_content = self.bundle.read()
|
||||||
|
self.deploy_parameters(bundle_content)
|
||||||
self.deploy_roles(bundle_content)
|
self.deploy_roles(bundle_content)
|
||||||
self.do_something_with_bundle(bundle_content, 'deploy', job=job)
|
self.do_something_with_bundle(bundle_content, 'deploy', job=job)
|
||||||
self.application.refresh_elements(cache_only=True)
|
self.application.refresh_elements(cache_only=True)
|
||||||
|
@ -365,6 +389,24 @@ class Version(models.Model):
|
||||||
return service
|
return service
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def deploy_parameters(self, bundle):
|
||||||
|
tar_io = io.BytesIO(bundle)
|
||||||
|
with tarfile.open(fileobj=tar_io) as tar:
|
||||||
|
manifest = json.loads(tar.extractfile('manifest.json').read().decode())
|
||||||
|
for parameter in manifest.get('parameters') or []:
|
||||||
|
param, _ = Parameter.objects.get_or_create(
|
||||||
|
name=parameter.get('name'), application=self.application
|
||||||
|
)
|
||||||
|
param.label = parameter.get('label')
|
||||||
|
param.default_value = parameter.get('default_value')
|
||||||
|
param.save()
|
||||||
|
variable, _ = Variable.objects.get_or_create(
|
||||||
|
name=parameter.get('name'),
|
||||||
|
defaults={'auto': True, 'value': parameter.get('default_value') or ''},
|
||||||
|
)
|
||||||
|
variable.label = parameter.get('label')
|
||||||
|
variable.save()
|
||||||
|
|
||||||
def deploy_roles(self, bundle):
|
def deploy_roles(self, bundle):
|
||||||
tar_io = io.BytesIO(bundle)
|
tar_io = io.BytesIO(bundle)
|
||||||
service = self.get_authentic_service()
|
service = self.get_authentic_service()
|
||||||
|
|
|
@ -76,5 +76,38 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% with has_parameters=app.parameter_set.exists parameters_qs=app.parameter_set.all %}
|
||||||
|
{% if app.editable or has_parameters %}
|
||||||
|
<div class="app-parameters--section">
|
||||||
|
<h3>{% trans "Parameters" %}
|
||||||
|
{% if app.editable %}
|
||||||
|
<a rel="popup" href="{% url 'application-add-parameter' app_slug=app.slug %}">({% trans "add" %})</a>
|
||||||
|
{% endif %}
|
||||||
|
</h3>
|
||||||
|
{% if app.editable and has_parameters %}
|
||||||
|
<ul class="objects-list single-links app-parameters--list">
|
||||||
|
{% for parameter in parameters_qs %}
|
||||||
|
<li><a rel="popup" href="{% url 'application-edit-parameter' app_slug=app.slug pk=parameter.id %}">{{ parameter.label }}</a>
|
||||||
|
<a rel="popup" class="delete" href="{% url 'application-delete-parameter' app_slug=app.slug pk=parameter.id %}">{% trans "remove" %}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% elif app.editable %}
|
||||||
|
<p>{% trans "No parameters defined." %}</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if has_parameters %}
|
||||||
|
<div class="app-parameters--values">
|
||||||
|
{% for parameter in parameters_qs %}
|
||||||
|
<p>
|
||||||
|
<label>{{ parameter.label }}</label>
|
||||||
|
{{ parameter.variable.value|default:"-" }}
|
||||||
|
(<a rel="popup" href="{% url 'application-change-parameter-value' app_slug=app.slug name=parameter.variable.name %}">{% trans 'change' %}</a>)
|
||||||
|
</p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
</aside>
|
</aside>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "hobo/applications/home.html" %}
|
||||||
|
{% load gadjo i18n %}
|
||||||
|
|
||||||
|
{% block appbar %}
|
||||||
|
<h2>{% trans "New Parameter" %}</h2>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form|with_template }}
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="submit-button">{% trans "Add" %}</button>
|
||||||
|
<a class="cancel" href="..">{% trans "Cancel" %}</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{% extends "hobo/applications/home.html" %}
|
||||||
|
{% load gadjo i18n %}
|
||||||
|
|
||||||
|
{% block appbar %}
|
||||||
|
<h2>{% trans "Delete Parameter" %}</h2>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<p>
|
||||||
|
{% trans 'Are you sure you want to remove this parameter?' %}
|
||||||
|
</p>
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="delete-button">{% trans "Delete" %}</button>
|
||||||
|
<a class="cancel" href="../../">{% trans "Cancel" %}</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "hobo/applications/home.html" %}
|
||||||
|
{% load gadjo i18n %}
|
||||||
|
|
||||||
|
{% block appbar %}
|
||||||
|
<h2>{% trans "Edit Parameter" %}</h2>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form|with_template }}
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="submit-button">{% trans "Save" %}</button>
|
||||||
|
<a class="cancel" href="../../">{% trans "Cancel" %}</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "hobo/applications/home.html" %}
|
||||||
|
{% load gadjo i18n %}
|
||||||
|
|
||||||
|
{% block appbar %}
|
||||||
|
<h2>{% trans "Edit Parameter" %}</h2>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form|with_template }}
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="submit-button">{% trans "Save" %}</button>
|
||||||
|
<a class="cancel" href="../../">{% trans "Cancel" %}</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
|
@ -45,6 +45,26 @@ urlpatterns = [
|
||||||
views.delete_element,
|
views.delete_element,
|
||||||
name='application-delete-element',
|
name='application-delete-element',
|
||||||
),
|
),
|
||||||
|
re_path(
|
||||||
|
r'^manifest/(?P<app_slug>[\w-]+)/add-parameter/$',
|
||||||
|
views.add_parameter,
|
||||||
|
name='application-add-parameter',
|
||||||
|
),
|
||||||
|
re_path(
|
||||||
|
r'^manifest/(?P<app_slug>[\w-]+)/edit-parameter/(?P<pk>\d+)/$',
|
||||||
|
views.edit_parameter,
|
||||||
|
name='application-edit-parameter',
|
||||||
|
),
|
||||||
|
re_path(
|
||||||
|
r'^manifest/(?P<app_slug>[\w-]+)/delete-parameter/(?P<pk>\d+)/$',
|
||||||
|
views.delete_parameter,
|
||||||
|
name='application-delete-parameter',
|
||||||
|
),
|
||||||
|
re_path(
|
||||||
|
r'^manifest/(?P<app_slug>[\w-]+)/parameter-value/(?P<name>[\w_]+)/$',
|
||||||
|
views.change_parameter_value,
|
||||||
|
name='application-change-parameter-value',
|
||||||
|
),
|
||||||
re_path(
|
re_path(
|
||||||
r'^manifest/(?P<app_slug>[\w-]+)/job/(?P<pk>\d+)/$',
|
r'^manifest/(?P<app_slug>[\w-]+)/job/(?P<pk>\d+)/$',
|
||||||
views.async_job,
|
views.async_job,
|
||||||
|
|
|
@ -31,12 +31,15 @@ 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, TemplateView
|
||||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||||
|
|
||||||
|
from hobo.environment.models import Variable
|
||||||
|
|
||||||
from .forms import GenerateForm, InstallForm, MetadataForm
|
from .forms import GenerateForm, InstallForm, MetadataForm
|
||||||
from .models import (
|
from .models import (
|
||||||
STATUS_CHOICES,
|
STATUS_CHOICES,
|
||||||
Application,
|
Application,
|
||||||
AsyncJob,
|
AsyncJob,
|
||||||
Element,
|
Element,
|
||||||
|
Parameter,
|
||||||
Relation,
|
Relation,
|
||||||
UnlinkError,
|
UnlinkError,
|
||||||
Version,
|
Version,
|
||||||
|
@ -451,3 +454,74 @@ class AsyncJobView(DetailView):
|
||||||
|
|
||||||
|
|
||||||
async_job = AsyncJobView.as_view()
|
async_job = AsyncJobView.as_view()
|
||||||
|
|
||||||
|
|
||||||
|
class AddParameterView(CreateView):
|
||||||
|
template_name = 'hobo/applications/parameter-add.html'
|
||||||
|
model = Parameter
|
||||||
|
fields = ['label', 'name', 'default_value']
|
||||||
|
|
||||||
|
def dispatch(self, *args, **kwargs):
|
||||||
|
self.application = get_object_or_404(Application, slug=kwargs['app_slug'], editable=True)
|
||||||
|
return super().dispatch(*args, **kwargs)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
form.instance.application = self.application
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse('application-manifest', kwargs={'app_slug': self.kwargs['app_slug']})
|
||||||
|
|
||||||
|
|
||||||
|
add_parameter = AddParameterView.as_view()
|
||||||
|
|
||||||
|
|
||||||
|
class EditParameterView(UpdateView):
|
||||||
|
template_name = 'hobo/applications/parameter-edit.html'
|
||||||
|
model = Parameter
|
||||||
|
fields = ['label', 'name', 'default_value']
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return (
|
||||||
|
super()
|
||||||
|
.get_queryset()
|
||||||
|
.filter(application__editable=True, application__slug=self.kwargs['app_slug'])
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse('application-manifest', kwargs={'app_slug': self.kwargs['app_slug']})
|
||||||
|
|
||||||
|
|
||||||
|
edit_parameter = EditParameterView.as_view()
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteParameterView(DeleteView):
|
||||||
|
template_name = 'hobo/applications/parameter-confirm-delete.html'
|
||||||
|
model = Parameter
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return (
|
||||||
|
super()
|
||||||
|
.get_queryset()
|
||||||
|
.filter(application__editable=True, application__slug=self.kwargs['app_slug'])
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse('application-manifest', kwargs={'app_slug': self.kwargs['app_slug']})
|
||||||
|
|
||||||
|
|
||||||
|
delete_parameter = DeleteParameterView.as_view()
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeParameterValueView(UpdateView):
|
||||||
|
template_name = 'hobo/applications/parameter-value-edit.html'
|
||||||
|
model = Variable
|
||||||
|
fields = ['value']
|
||||||
|
slug_field = 'name'
|
||||||
|
slug_url_kwarg = 'name'
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse('application-manifest', kwargs={'app_slug': self.kwargs['app_slug']})
|
||||||
|
|
||||||
|
|
||||||
|
change_parameter_value = ChangeParameterValueView.as_view()
|
||||||
|
|
|
@ -381,3 +381,8 @@ ul.objects-list.application-content {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.app-parameters--values label {
|
||||||
|
display: block;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
|
@ -17,11 +17,12 @@ from hobo.applications.models import (
|
||||||
AsyncJob,
|
AsyncJob,
|
||||||
DeploymentError,
|
DeploymentError,
|
||||||
Element,
|
Element,
|
||||||
|
Parameter,
|
||||||
Relation,
|
Relation,
|
||||||
ScanError,
|
ScanError,
|
||||||
Version,
|
Version,
|
||||||
)
|
)
|
||||||
from hobo.environment.models import Authentic, Wcs
|
from hobo.environment.models import Authentic, Variable, Wcs
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
@ -822,6 +823,10 @@ def get_bundle(with_icon=False):
|
||||||
'auto-dependency': True,
|
'auto-dependency': True,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
'parameters': [
|
||||||
|
{'name': 'app_param1', 'label': 'Parameter 1', 'default_value': 'plop'},
|
||||||
|
{'name': 'app_param2', 'label': 'Parameter 2', 'default_value': None},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
manifest_fd = io.BytesIO(json.dumps(manifest_json, indent=2).encode())
|
manifest_fd = io.BytesIO(json.dumps(manifest_json, indent=2).encode())
|
||||||
tarinfo = tarfile.TarInfo('manifest.json')
|
tarinfo = tarfile.TarInfo('manifest.json')
|
||||||
|
@ -883,6 +888,8 @@ def test_deploy_application(app, admin_user, settings, app_bundle, app_bundle_wi
|
||||||
job = AsyncJob.objects.latest('pk')
|
job = AsyncJob.objects.latest('pk')
|
||||||
assert job.status == 'completed'
|
assert job.status == 'completed'
|
||||||
assert job.progression_urls == {'wcs': {'Foobar': 'https://wcs.example.invalid/api/jobs/job-uuid/'}}
|
assert job.progression_urls == {'wcs': {'Foobar': 'https://wcs.example.invalid/api/jobs/job-uuid/'}}
|
||||||
|
assert Parameter.objects.filter(application=application).count() == 2
|
||||||
|
assert Variable.objects.filter(name__startswith='app_').count() == 2
|
||||||
return version
|
return version
|
||||||
|
|
||||||
resp = app.get('/applications/')
|
resp = app.get('/applications/')
|
||||||
|
@ -1247,3 +1254,112 @@ def test_job_status_page(app, admin_user, settings):
|
||||||
job.refresh_from_db()
|
job.refresh_from_db()
|
||||||
assert job.status == 'failed'
|
assert job.status == 'failed'
|
||||||
assert job.exception == 'Failed to deploy module wcs'
|
assert job.exception == 'Failed to deploy module wcs'
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_application_parameters(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)
|
||||||
|
|
||||||
|
resp = app.get('/applications/')
|
||||||
|
resp = resp.click('Create')
|
||||||
|
resp.form['name'] = 'Test'
|
||||||
|
resp = resp.form.submit()
|
||||||
|
|
||||||
|
with HTTMock(mocked_http):
|
||||||
|
resp = resp.follow()
|
||||||
|
|
||||||
|
# add a form (an element is required to have the generate app button)
|
||||||
|
resp = resp.click('Forms')
|
||||||
|
resp.form.fields['elements'][4].checked = True
|
||||||
|
resp = resp.form.submit().follow()
|
||||||
|
|
||||||
|
assert resp.pyquery('.app-parameters--section h3 a')
|
||||||
|
resp = resp.click('add')
|
||||||
|
resp.form['label'] = 'Foo'
|
||||||
|
resp.form['name'] = 'app_foo'
|
||||||
|
resp.form['default_value'] = 'xxx'
|
||||||
|
resp = resp.form.submit().follow()
|
||||||
|
assert Parameter.objects.filter(name='app_foo').exists()
|
||||||
|
assert resp.pyquery('.app-parameters--list li')
|
||||||
|
resp = resp.click(href=resp.pyquery('.app-parameters--list li a:not(.delete)').attr.href)
|
||||||
|
assert resp.form['label'].value == 'Foo'
|
||||||
|
assert resp.form['name'].value == 'app_foo'
|
||||||
|
assert resp.form['default_value'].value == 'xxx'
|
||||||
|
resp = resp.click('Cancel')
|
||||||
|
|
||||||
|
resp = resp.click(href=resp.pyquery('.app-parameters--values a').attr.href)
|
||||||
|
assert resp.form['value'].value == 'xxx'
|
||||||
|
resp.form['value'] = 'changed'
|
||||||
|
resp = resp.form.submit().follow()
|
||||||
|
assert Variable.objects.get(name='app_foo').value == 'changed'
|
||||||
|
|
||||||
|
resp = resp.click('Generate application bundle')
|
||||||
|
resp.form['number'] = '1.0'
|
||||||
|
resp.form['notes'] = 'Foo bar blah.'
|
||||||
|
resp = resp.form.submit().follow()
|
||||||
|
resp_download = resp.click('Download')
|
||||||
|
assert resp_download.content_type == 'application/x-tar'
|
||||||
|
# uncompressed tar, primitive check of contents
|
||||||
|
assert b'app_foo' in resp_download.content
|
||||||
|
|
||||||
|
resp = resp.click(href=resp.pyquery('.app-parameters--list li a.delete').attr.href)
|
||||||
|
resp = resp.form.submit().follow()
|
||||||
|
assert not Parameter.objects.filter(name='app_foo').exists()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def app_bundle_parameters():
|
||||||
|
tar_io = io.BytesIO()
|
||||||
|
with tarfile.open(mode='w', fileobj=tar_io) as tar:
|
||||||
|
manifest_json = {
|
||||||
|
'application': 'Test',
|
||||||
|
'slug': 'test',
|
||||||
|
'description': '',
|
||||||
|
'elements': [],
|
||||||
|
'parameters': [{"label": "Foo", "name": "app_foo", "default_value": "xxx"}],
|
||||||
|
}
|
||||||
|
manifest_fd = io.BytesIO(json.dumps(manifest_json, indent=2).encode())
|
||||||
|
tarinfo = tarfile.TarInfo('manifest.json')
|
||||||
|
tarinfo.size = len(manifest_fd.getvalue())
|
||||||
|
tar.addfile(tarinfo, fileobj=manifest_fd)
|
||||||
|
return tar_io.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
def test_deploy_application_parameters(app, admin_user, settings, app_bundle_parameters):
|
||||||
|
Parameter.objects.all().delete()
|
||||||
|
Variable.objects.all().delete()
|
||||||
|
Application.objects.all().delete()
|
||||||
|
|
||||||
|
login(app)
|
||||||
|
|
||||||
|
resp = app.get('/applications/')
|
||||||
|
resp = resp.click('Install')
|
||||||
|
resp.form['bundle'] = Upload('app.tar', app_bundle_parameters, 'application/x-tar')
|
||||||
|
resp = resp.form.submit().follow()
|
||||||
|
assert Variable.objects.get(name='app_foo').value == 'xxx'
|
||||||
|
resp = resp.click('Details')
|
||||||
|
assert resp.pyquery('.app-parameters--section h3')
|
||||||
|
assert not resp.pyquery('.app-parameters--section h3 a')
|
||||||
|
|
||||||
|
resp = resp.click(href=resp.pyquery('.app-parameters--values a').attr.href)
|
||||||
|
assert resp.form['value'].value == 'xxx'
|
||||||
|
resp.form['value'] = 'changed'
|
||||||
|
resp = resp.form.submit().follow()
|
||||||
|
assert Variable.objects.get(name='app_foo').value == 'changed'
|
||||||
|
|
||||||
|
# check parameter cannot be edited or deleted
|
||||||
|
parameter = Parameter.objects.all().first()
|
||||||
|
resp = app.get(f'/applications/manifest/test/edit-parameter/{parameter.id}/', status=404)
|
||||||
|
resp = app.get(f'/applications/manifest/test/delete-parameter/{parameter.id}/', status=404)
|
||||||
|
|
Loading…
Reference in New Issue