environment: import and export parameters (#33672)

This commit is contained in:
Nicolas Roche 2020-10-14 17:13:52 +02:00
parent 3ead547aa1
commit 60e5bd28d0
6 changed files with 193 additions and 3 deletions

View File

@ -210,3 +210,7 @@ class VariablesFormMixin(object):
messages.info(self.request, self.success_message)
return HttpResponseRedirect('.')
class ImportForm(forms.Form):
parameters_json = forms.FileField(label=_('Parameters Export File'))

View File

@ -34,5 +34,8 @@ urlpatterns = [
url(r'^new-variable-(?P<service>\w+)/(?P<slug>[\w-]+)$',
views.VariableCreateView.as_view(), name='new-variable-service',),
url(r'^import/$', views.ImportView.as_view(), name='environment-import'),
url(r'^export/$', views.ExportView.as_view(), name='environment-export'),
url(r'^debug.json$', views.debug_json, name='debug-json'),
]

View File

@ -18,12 +18,13 @@ import hashlib
from django.conf import settings
from django.urls import reverse
from django.db import connection
from django.db import connection, transaction
from django.utils.six.moves.urllib.parse import urlparse
from django.utils.encoding import force_text
from hobo.middleware.utils import StoreRequestMiddleware
from hobo.multitenant.settings_loaders import KnownServices
from hobo.profile.utils import get_profile_dict
def get_installed_services():
@ -144,3 +145,36 @@ def get_setting_variable(setting_name, label=None, service=None):
auto=True)
return variable
def export_parameters():
from .models import Variable
variables = []
for var in Variable.objects.filter(service_pk__isnull=True):
variables.append({
'name': var.name,
'label': var.label,
'value': var.value,
'auto': var.auto})
parameters = {'variables': variables}
parameters.update(get_profile_dict())
return parameters
def import_parameters(parameters):
from .models import Variable
from hobo.profile.models import AttributeDefinition
with transaction.atomic():
for variables in parameters.get('variables', []):
obj, created = Variable.objects.get_or_create(name=variables['name'])
for key, value in variables.items():
setattr(obj, key, value)
obj.save()
for fields in parameters.get('profile', {}).get('fields', []):
obj, created = AttributeDefinition.objects.get_or_create(name=fields['name'])
for key, value in fields.items():
setattr(obj, key, value)
obj.save()

View File

@ -20,10 +20,13 @@ import string
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.urls import reverse_lazy
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.http import HttpResponse, HttpResponseRedirect, Http404, JsonResponse
from django.shortcuts import get_object_or_404
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
from django.views.generic import View
from django.views.generic.base import TemplateView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
from .models import Variable, AVAILABLE_SERVICES
from . import forms, utils
@ -199,6 +202,31 @@ class ServiceDeleteView(DeleteView):
return None
class ImportView(FormView):
form_class = forms.ImportForm
template_name = 'environment/import.html'
success_url = reverse_lazy('home')
def form_valid(self, form):
try:
parameters_json = json.loads(
force_text(self.request.FILES['parameters_json'].read()))
except ValueError:
form.add_error('parameters_json', _('File is not in the expected JSON format.'))
return self.form_invalid(form)
utils.import_parameters(parameters_json)
return super(ImportView, self).form_valid(form)
class ExportView(View):
def get(self, request, *args, **kwargs):
response = JsonResponse(utils.export_parameters(), json_dumps_params={'indent': 2})
response['Content-Disposition'] = 'attachment; filename="hobo-export.json"'
return response
def operational_check_view(request, service, slug, **kwargs):
for klass in AVAILABLE_SERVICES:

View File

@ -19,6 +19,8 @@
<li><a href="{% url 'seo-home' %}">{% trans 'Indexing' %}</a></li>
<li><a href="{% url 'environment-home' %}">{% trans 'Services' %}</a></li>
<li><a href="{% url 'environment-variables' %}">{% trans 'Variables' %}</a></li>
<li><a rel="popup" href="{% url 'environment-import' %}">{% trans 'Import' %}</a></li>
<li><a href="{% url 'environment-export' %}">{% trans 'Export' %}</a></li>
<li><a href="{% url 'debug-home' %}">{% trans 'Debugging' %}</a></li>
</ul>
</span>

View File

@ -1,11 +1,15 @@
# -*- coding: utf-8 -*-
import json
import pytest
from webtest import Upload
from django.core.exceptions import ValidationError
from django.core.management import call_command
from django.db.utils import IntegrityError
from django.utils import timezone
from hobo.environment.models import AVAILABLE_SERVICES, Combo, Passerelle, ServiceBase, Variable
from hobo.profile.models import AttributeDefinition
from test_manager import login
@ -316,3 +320,118 @@ def test_check_operational_command(monkeypatch, capsys):
'foo is NOT operational',
' last operational success: 2022-02-22 00:00:00+00:00'
]
def test_export_import_view(app, admin_user):
combo = Combo.objects.create(base_url='https://combo.agglo.love',
template_name='...portal-user...',
slug='portal')
Variable.objects.create(name='foo', value='bar').save()
Variable.objects.create(name='foo2', value='bar2', service=combo).save()
app = login(app, 'admin', 'password')
resp = app.get('/sites/export/', status=200)
assert sorted(resp.json.keys()) == ['profile', 'variables']
assert resp.json['variables'] == [
{'name': 'foo', 'label': '', 'value': 'bar', 'auto': False}]
assert resp.json['profile']['fields'][0]['name'] == 'title'
assert resp.json['profile']['fields'][0]['required'] is False
assert resp.json['profile']['fields'][0]['description'] == ''
assert resp.json['profile']['fields'][2]['name'] == 'last_name'
assert resp.json['profile']['fields'][2]['required'] is True
assert resp.json['profile']['fields'][2]['label'] == 'Nom'
# modify exported file
export = resp.json
export['variables'][0]['label'] = 'bar'
fields = export['profile']['fields']
assert fields[0]['name'] == 'title'
assert fields[2]['name'] == 'last_name'
fields[0]['description'] = 'genre'
fields[2]['label'] = 'Nom de naissance'
fields[0], fields[2] = fields[2], fields[0]
export_json = json.dumps(export)
# add new content
Variable.objects.create(name='foo3', value='bar3').save()
AttributeDefinition.objects.create(name='prefered_color', label='not empty').save()
assert Variable.objects.count() == 3
assert AttributeDefinition.objects.count() == 12
assert Variable.objects.get(name='foo').label == ''
assert AttributeDefinition.objects.get(name='title').description == ''
assert AttributeDefinition.objects.get(name='title').order == 1
assert AttributeDefinition.objects.get(name='last_name').order == 3
assert AttributeDefinition.objects.get(name='prefered_color').order == 12
# import valid content
resp = app.get('/', status=200)
resp = resp.click('Import')
resp.form['parameters_json'] = Upload(
'export.json', export_json.encode('utf-8'), 'application/json')
resp = resp.form.submit()
assert Variable.objects.count() == 3
assert AttributeDefinition.objects.count() == 12
assert Variable.objects.get(name='foo').label == 'bar'
assert AttributeDefinition.objects.get(name='title').description == 'genre'
assert AttributeDefinition.objects.get(name='title').order == 1
assert AttributeDefinition.objects.get(name='last_name').label == 'Nom de naissance'
assert AttributeDefinition.objects.get(name='last_name').order == 3
assert AttributeDefinition.objects.get(name='prefered_color').order == 12
# import empty json
resp = app.get('/', status=200)
resp = resp.click('Import')
resp.form['parameters_json'] = Upload(
'export.json', b'{}', 'application/json')
resp = resp.form.submit()
assert Variable.objects.count() == 3
assert AttributeDefinition.objects.count() == 12
assert Variable.objects.get(name='foo').label == 'bar'
assert AttributeDefinition.objects.get(name='title').description == 'genre'
assert AttributeDefinition.objects.get(name='title').order == 1
assert AttributeDefinition.objects.get(name='last_name').label == 'Nom de naissance'
assert AttributeDefinition.objects.get(name='last_name').order == 3
assert AttributeDefinition.objects.get(name='prefered_color').order == 12
# import from scratch
Variable.objects.all().delete()
AttributeDefinition.objects.all().delete()
Variable.objects.create(name='foo2', value='bar2', service=combo).save()
AttributeDefinition.objects.create(name='prefered_color', label='not empty').save()
assert Variable.objects.count() == 1
assert AttributeDefinition.objects.count() == 1
resp = app.get('/', status=200)
resp = resp.click('Import')
resp.form['parameters_json'] = Upload(
'export.json', export_json.encode('utf-8'), 'application/json')
resp = resp.form.submit()
assert Variable.objects.count() == 2
assert AttributeDefinition.objects.count() == 12
assert Variable.objects.get(name='foo').label == 'bar'
assert AttributeDefinition.objects.get(name='title').order == 4
assert AttributeDefinition.objects.get(name='last_name').order == 2
assert AttributeDefinition.objects.get(name='prefered_color').order == 1
# import invalid json
resp = app.get('/', status=200)
resp = resp.click('Import')
resp.form['parameters_json'] = Upload(
'export.json', b'garbage', 'application/json')
resp = resp.form.submit()
assert Variable.objects.count() == 2
assert AttributeDefinition.objects.count() == 12
# import corrupted json
export['variables'][0]['label'] = 'foofoo'
fields = export['profile']['fields']
assert fields[2]['name'] == 'title'
fields[2]['label'] = 'Nom de naissance'
export_json = json.dumps(export)
resp = app.get('/', status=200)
resp = resp.click('Import')
resp.form['parameters_json'] = Upload(
'export.json', export_json.encode('utf-8'), 'application/json')
with pytest.raises(IntegrityError):
resp = resp.form.submit()
assert Variable.objects.count() == 2
assert AttributeDefinition.objects.count() == 12
assert Variable.objects.get(name='foo').label == 'bar'