applification: implémenter le endpoint bundle-check (#87653) #218
|
@ -19,6 +19,7 @@ import json
|
|||
import tarfile
|
||||
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.http import Http404
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.urls import reverse
|
||||
|
@ -41,6 +42,14 @@ klasses_translation = {
|
|||
}
|
||||
klasses_translation_reverse = {v: k for k, v in klasses_translation.items()}
|
||||
|
||||
compare_urls = {
|
||||
'agendas': 'chrono-manager-agenda-history-compare',
|
||||
'categories': 'chrono-manager-category-history-compare',
|
||||
'events_types': 'chrono-manager-events-type-history-compare',
|
||||
'resources': 'chrono-manager-resource-history-compare',
|
||||
'unavailability_calendars': 'chrono-manager-unavailability-calendar-history-compare',
|
||||
}
|
||||
|
||||
|
||||
class Index(GenericAPIView):
|
||||
permission_classes = (permissions.IsAdminUser,)
|
||||
|
@ -183,6 +192,27 @@ component_dependencies = ComponentDependencies.as_view()
|
|||
def component_redirect(request, component_type, slug):
|
||||
klass = klasses[component_type]
|
||||
component = get_object_or_404(klass, slug=slug)
|
||||
|
||||
if component_type not in klasses or component_type == 'roles':
|
||||
raise Http404
|
||||
|
||||
if (
|
||||
'compare' in request.GET
|
||||
and request.GET.get('application')
|
||||
and request.GET.get('version1')
|
||||
and request.GET.get('version2')
|
||||
):
|
||||
component_type = klasses_translation.get(component_type, component_type)
|
||||
return redirect(
|
||||
'%s?version1=%s&version2=%s&application=%s'
|
||||
% (
|
||||
reverse(compare_urls[component_type], args=[component.pk]),
|
||||
request.GET['version1'],
|
||||
request.GET['version2'],
|
||||
request.GET['application'],
|
||||
)
|
||||
)
|
||||
|
||||
if klass == Agenda:
|
||||
return redirect(reverse('chrono-manager-agenda-view', kwargs={'pk': component.pk}))
|
||||
if klass == Category:
|
||||
|
@ -200,7 +230,108 @@ class BundleCheck(GenericAPIView):
|
|||
permission_classes = (permissions.IsAdminUser,)
|
||||
|
||||
def put(self, request, *args, **kwargs):
|
||||
return Response({'err': 0, 'data': {}})
|
||||
tar_io = io.BytesIO(request.read())
|
||||
with tarfile.open(fileobj=tar_io) as tar:
|
||||
manifest = json.loads(tar.extractfile('manifest.json').read().decode())
|
||||
application_slug = manifest.get('slug')
|
||||
application_version = manifest.get('version_number')
|
||||
if not application_slug or not application_version:
|
||||
return Response({'data': {}})
|
||||
|
||||
differences = []
|
||||
unknown_elements = []
|
||||
no_history_elements = []
|
||||
legacy_elements = []
|
||||
content_types = ContentType.objects.get_for_models(
|
||||
*[v for k, v in klasses.items() if k != 'roles']
|
||||
)
|
||||
for element in manifest.get('elements'):
|
||||
component_type = element['type']
|
||||
if component_type not in klasses or component_type == 'roles':
|
||||
continue
|
||||
klass = klasses[component_type]
|
||||
component_type = klasses_translation.get(component_type, component_type)
|
||||
try:
|
||||
component = klass.objects.get(slug=element['slug'])
|
||||
except klass.DoesNotExist:
|
||||
unknown_elements.append(
|
||||
{
|
||||
'type': component_type,
|
||||
'slug': element['slug'],
|
||||
}
|
||||
)
|
||||
continue
|
||||
elements_qs = ApplicationElement.objects.filter(
|
||||
application__slug=application_slug,
|
||||
content_type=content_types[klass],
|
||||
object_id=component.pk,
|
||||
)
|
||||
if not elements_qs.exists():
|
||||
# object exists, but not linked to the application
|
||||
legacy_elements.append(
|
||||
{
|
||||
'type': component.application_component_type,
|
||||
'slug': str(component.slug),
|
||||
# information needed here, Relation objects may not exist yet in hobo
|
||||
'text': component.label,
|
||||
'url': reverse(
|
||||
'api-export-import-component-redirect',
|
||||
kwargs={
|
||||
pmarillonnet marked this conversation as resolved
Outdated
|
||||
'slug': str(component.slug),
|
||||
'component_type': component.application_component_type,
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
continue
|
||||
snapshot_for_app = (
|
||||
klass.get_snapshot_model()
|
||||
.objects.filter(
|
||||
instance=component,
|
||||
application_slug=application_slug,
|
||||
application_version=application_version,
|
||||
)
|
||||
.order_by('timestamp')
|
||||
.last()
|
||||
)
|
||||
if not snapshot_for_app:
|
||||
# no snapshot for this bundle
|
||||
no_history_elements.append(
|
||||
{
|
||||
'type': element['type'],
|
||||
'slug': element['slug'],
|
||||
pmarillonnet marked this conversation as resolved
Outdated
pmarillonnet
commented
Ok, un peu surpris de la brévité des informations contenues dans ce Ok, un peu surpris de la brévité des informations contenues dans ce `no_history_elements`, par exemple en comparaison des `legacy_elements` qui présentent un libellé humainement intelligible et une url pour l’objet. J’imagine que l’usage final n’est pas le même.
lguerin
commented
Comme dit plus haut, un legacy_element existe dans la brique mais pas encore dans hobo, donc on a besoin de renvoyer des informations permettant à hobo d'afficher un joli message et une url redirect. Comme dit plus haut, un legacy_element existe dans la brique mais pas encore dans hobo, donc on a besoin de renvoyer des informations permettant à hobo d'afficher un joli message et une url redirect.
Un no_history_element existe forcément dans hobo.
pmarillonnet
commented
Ok, capté, merci. Ok, capté, merci.
|
||||
}
|
||||
)
|
||||
continue
|
||||
last_snapshot = (
|
||||
klass.get_snapshot_model().objects.filter(instance=component).latest('timestamp')
|
||||
)
|
||||
if snapshot_for_app.pk != last_snapshot.pk:
|
||||
differences.append(
|
||||
{
|
||||
'type': element['type'],
|
||||
'slug': element['slug'],
|
||||
'url': '%s?version1=%s&version2=%s'
|
||||
% (
|
||||
request.build_absolute_uri(
|
||||
reverse(compare_urls[component_type], args=[component.pk])
|
||||
),
|
||||
pmarillonnet marked this conversation as resolved
Outdated
pmarillonnet
commented
Pas compris pourquoi on concatène des bouts d’urls au lieu de simplement taper un
Pas compris pourquoi on concatène des bouts d’urls au lieu de simplement taper un
``` python
request.build_absolute_uri(reverse(compare_urls[…], …))
```
lguerin
commented
en effet, corrigé en effet, corrigé
|
||||
snapshot_for_app.pk,
|
||||
last_snapshot.pk,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
return Response(
|
||||
{
|
||||
'data': {
|
||||
'differences': differences,
|
||||
'unknown_elements': unknown_elements,
|
||||
'no_history_elements': no_history_elements,
|
||||
'legacy_elements': legacy_elements,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
bundle_check = BundleCheck.as_view()
|
||||
|
@ -223,7 +354,7 @@ class BundleImport(GenericAPIView):
|
|||
|
||||
for element in manifest.get('elements'):
|
||||
component_type = element['type']
|
||||
if component_type not in klasses or element['type'] == 'roles':
|
||||
if component_type not in klasses or component_type == 'roles':
|
||||
continue
|
||||
component_type = klasses_translation.get(component_type, component_type)
|
||||
if component_type not in components:
|
||||
|
@ -260,6 +391,11 @@ class BundleImport(GenericAPIView):
|
|||
self.application, existing_component
|
||||
)
|
||||
self.application_elements.add(element.content_object)
|
||||
if self.install is True:
|
||||
existing_component.take_snapshot(
|
||||
comment=_('Application (%s)') % self.application,
|
||||
application=self.application,
|
||||
)
|
||||
|
||||
def unlink_obsolete_objects(self):
|
||||
known_elements = ApplicationElement.objects.filter(application=self.application)
|
||||
|
|
|
@ -54,7 +54,38 @@ class InstanceWithSnapshotHistoryView(ListView):
|
|||
|
||||
|
||||
class InstanceWithSnapshotHistoryCompareView(DetailView):
|
||||
def get_snapshots_from_application(self):
|
||||
version1 = self.request.GET.get('version1')
|
||||
version2 = self.request.GET.get('version2')
|
||||
if not version1 or not version2:
|
||||
raise Http404
|
||||
|
||||
snapshot_for_app1 = (
|
||||
self.model.get_snapshot_model()
|
||||
.objects.filter(
|
||||
instance=self.object,
|
||||
application_slug=self.request.GET['application'],
|
||||
application_version=self.request.GET['version1'],
|
||||
)
|
||||
.order_by('timestamp')
|
||||
.last()
|
||||
)
|
||||
snapshot_for_app2 = (
|
||||
self.model.get_snapshot_model()
|
||||
.objects.filter(
|
||||
instance=self.object,
|
||||
application_slug=self.request.GET['application'],
|
||||
application_version=self.request.GET['version2'],
|
||||
)
|
||||
.order_by('timestamp')
|
||||
.last()
|
||||
)
|
||||
return snapshot_for_app1, snapshot_for_app2
|
||||
|
||||
def get_snapshots(self):
|
||||
if 'application' in self.request.GET:
|
||||
return self.get_snapshots_from_application()
|
||||
|
||||
id1 = self.request.GET.get('version1')
|
||||
id2 = self.request.GET.get('version2')
|
||||
if not id1 or not id2:
|
||||
|
|
|
@ -9,6 +9,7 @@ from django.contrib.contenttypes.models import ContentType
|
|||
|
||||
from chrono.agendas.models import Agenda, Category, Desk, EventsType, Resource, UnavailabilityCalendar
|
||||
from chrono.apps.export_import.models import Application, ApplicationElement
|
||||
from chrono.apps.snapshot.models import AgendaSnapshot
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
@ -374,22 +375,57 @@ def test_redirect(app, user):
|
|||
redirect_url = f'/api/export-import/agendas/{agenda.slug}/redirect/'
|
||||
resp = app.get(redirect_url, status=302)
|
||||
assert resp.location == f'/manage/agendas/{agenda.pk}/'
|
||||
resp = app.get(redirect_url + '?compare', status=302)
|
||||
assert resp.location == f'/manage/agendas/{agenda.pk}/'
|
||||
resp = app.get(redirect_url + '?compare&version1=bar&version2=bar&application=foo', status=302)
|
||||
assert (
|
||||
resp.location
|
||||
== f'/manage/agendas/{agenda.pk}/history/compare/?version1=bar&version2=bar&application=foo'
|
||||
)
|
||||
|
||||
redirect_url = f'/api/export-import/agendas_categories/{category.slug}/redirect/'
|
||||
resp = app.get(redirect_url, status=302)
|
||||
assert resp.location == '/manage/categories/'
|
||||
resp = app.get(redirect_url + '?compare', status=302)
|
||||
assert resp.location == '/manage/categories/'
|
||||
resp = app.get(redirect_url + '?compare&version1=bar&version2=bar&application=foo', status=302)
|
||||
assert (
|
||||
resp.location
|
||||
== f'/manage/category/{category.pk}/history/compare/?version1=bar&version2=bar&application=foo'
|
||||
)
|
||||
|
||||
redirect_url = f'/api/export-import/resources/{resource.slug}/redirect/'
|
||||
resp = app.get(redirect_url, status=302)
|
||||
assert resp.location == f'/manage/resource/{resource.pk}/'
|
||||
resp = app.get(redirect_url + '?compare', status=302)
|
||||
assert resp.location == f'/manage/resource/{resource.pk}/'
|
||||
resp = app.get(redirect_url + '?compare&version1=bar&version2=bar&application=foo', status=302)
|
||||
assert (
|
||||
resp.location
|
||||
== f'/manage/resource/{resource.pk}/history/compare/?version1=bar&version2=bar&application=foo'
|
||||
)
|
||||
|
||||
redirect_url = f'/api/export-import/events_types/{events_type.slug}/redirect/'
|
||||
resp = app.get(redirect_url, status=302)
|
||||
assert resp.location == '/manage/events-types/'
|
||||
resp = app.get(redirect_url + '?compare', status=302)
|
||||
assert resp.location == '/manage/events-types/'
|
||||
resp = app.get(redirect_url + '?compare&version1=bar&version2=bar&application=foo', status=302)
|
||||
assert (
|
||||
resp.location
|
||||
== f'/manage/events-type/{events_type.pk}/history/compare/?version1=bar&version2=bar&application=foo'
|
||||
)
|
||||
|
||||
redirect_url = f'/api/export-import/unavailability_calendars/{unavailability_calendar.slug}/redirect/'
|
||||
resp = app.get(redirect_url, status=302)
|
||||
assert resp.location == f'/manage/unavailability-calendar/{unavailability_calendar.pk}/'
|
||||
resp = app.get(redirect_url + '?compare', status=302)
|
||||
assert resp.location == f'/manage/unavailability-calendar/{unavailability_calendar.pk}/'
|
||||
resp = app.get(redirect_url + '?compare&version1=bar&version2=bar&application=foo', status=302)
|
||||
assert (
|
||||
resp.location
|
||||
== f'/manage/unavailability-calendar/{unavailability_calendar.pk}/history/compare/?version1=bar&version2=bar&application=foo'
|
||||
)
|
||||
|
||||
|
||||
def create_bundle(app, admin_user, visible=True, version_number='42.0'):
|
||||
|
@ -506,6 +542,12 @@ def test_bundle_import(app, admin_user):
|
|||
assert application.editable is False
|
||||
assert application.visible is True
|
||||
assert ApplicationElement.objects.count() == 8
|
||||
for model in [Agenda, Category, EventsType, Resource, UnavailabilityCalendar]:
|
||||
for instance in model.objects.all():
|
||||
last_snapshot = model.get_snapshot_model().objects.filter(instance=instance).latest('pk')
|
||||
assert last_snapshot.comment == 'Application (Test)'
|
||||
assert last_snapshot.application_slug == 'test'
|
||||
assert last_snapshot.application_version == '42.0'
|
||||
|
||||
# check editable flag is kept on install
|
||||
application.editable = True
|
||||
|
@ -535,6 +577,12 @@ def test_bundle_import(app, admin_user):
|
|||
).exists()
|
||||
is False
|
||||
)
|
||||
for model in [Agenda, Category, EventsType, Resource, UnavailabilityCalendar]:
|
||||
for instance in model.objects.all():
|
||||
last_snapshot = model.get_snapshot_model().objects.filter(instance=instance).latest('pk')
|
||||
assert last_snapshot.comment == 'Application (Test)'
|
||||
assert last_snapshot.application_slug == 'test'
|
||||
assert last_snapshot.application_version == '42.1'
|
||||
|
||||
|
||||
def test_bundle_declare(app, admin_user):
|
||||
|
@ -632,4 +680,267 @@ def test_bundle_unlink(app, admin_user, bundle):
|
|||
|
||||
def test_bundle_check(app, admin_user):
|
||||
app.authorization = ('Basic', ('admin', 'admin'))
|
||||
assert app.put('/api/export-import/bundle-check/').json == {'err': 0, 'data': {}}
|
||||
|
||||
bundles = []
|
||||
for version_number in ['42.0', '42.1']:
|
||||
bundles.append(create_bundle(app, admin_user, version_number=version_number))
|
||||
Agenda.objects.all().delete()
|
||||
Category.objects.all().delete()
|
||||
Resource.objects.all().delete()
|
||||
EventsType.objects.all().delete()
|
||||
UnavailabilityCalendar.objects.all().delete()
|
||||
|
||||
incomplete_bundles = []
|
||||
for manifest_json in [{'slug': 'test'}, {'version_number': '1.0'}]:
|
||||
tar_io = io.BytesIO()
|
||||
with tarfile.open(mode='w', fileobj=tar_io) as tar:
|
||||
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)
|
||||
incomplete_bundles.append(tar_io.getvalue())
|
||||
|
||||
# incorrect bundles, missing information
|
||||
resp = app.put('/api/export-import/bundle-check/', incomplete_bundles[0])
|
||||
assert resp.json == {'data': {}}
|
||||
resp = app.put('/api/export-import/bundle-check/', incomplete_bundles[1])
|
||||
assert resp.json == {'data': {}}
|
||||
|
||||
# not yet imported
|
||||
resp = app.put('/api/export-import/bundle-check/', bundles[0])
|
||||
assert resp.json == {
|
||||
'data': {
|
||||
'differences': [],
|
||||
'no_history_elements': [],
|
||||
'unknown_elements': [
|
||||
{'slug': 'rdv', 'type': 'agendas'},
|
||||
{'slug': 'foo', 'type': 'categories'},
|
||||
{'slug': 'foo', 'type': 'resources'},
|
||||
{'slug': 'foo', 'type': 'unavailability_calendars'},
|
||||
{'slug': 'evt', 'type': 'agendas'},
|
||||
{'slug': 'foo', 'type': 'events_types'},
|
||||
{'slug': 'virt', 'type': 'agendas'},
|
||||
{'slug': 'sub', 'type': 'agendas'},
|
||||
],
|
||||
'legacy_elements': [],
|
||||
}
|
||||
}
|
||||
|
||||
# import bundle
|
||||
resp = app.put('/api/export-import/bundle-import/', bundles[0])
|
||||
assert Application.objects.count() == 1
|
||||
assert ApplicationElement.objects.count() == 8
|
||||
|
||||
# remove application links
|
||||
Application.objects.all().delete()
|
||||
resp = app.put('/api/export-import/bundle-check/', bundles[0])
|
||||
assert resp.json == {
|
||||
'data': {
|
||||
'differences': [],
|
||||
'no_history_elements': [],
|
||||
'unknown_elements': [],
|
||||
'legacy_elements': [
|
||||
{
|
||||
'slug': 'rdv',
|
||||
'text': 'Rdv',
|
||||
'type': 'agendas',
|
||||
'url': '/api/export-import/agendas/rdv/redirect/',
|
||||
},
|
||||
{
|
||||
'slug': 'foo',
|
||||
'text': 'Foo',
|
||||
'type': 'agendas_categories',
|
||||
'url': '/api/export-import/agendas_categories/foo/redirect/',
|
||||
},
|
||||
{
|
||||
'slug': 'foo',
|
||||
'text': 'Foo',
|
||||
'type': 'resources',
|
||||
'url': '/api/export-import/resources/foo/redirect/',
|
||||
},
|
||||
{
|
||||
'slug': 'foo',
|
||||
'text': 'Foo',
|
||||
'type': 'unavailability_calendars',
|
||||
'url': '/api/export-import/unavailability_calendars/foo/redirect/',
|
||||
},
|
||||
{
|
||||
'slug': 'evt',
|
||||
'text': 'Evt',
|
||||
'type': 'agendas',
|
||||
'url': '/api/export-import/agendas/evt/redirect/',
|
||||
},
|
||||
{
|
||||
'slug': 'foo',
|
||||
'text': 'Foo',
|
||||
'type': 'events_types',
|
||||
'url': '/api/export-import/events_types/foo/redirect/',
|
||||
},
|
||||
{
|
||||
'slug': 'virt',
|
||||
'text': 'Virt',
|
||||
'type': 'agendas',
|
||||
'url': '/api/export-import/agendas/virt/redirect/',
|
||||
},
|
||||
{
|
||||
'slug': 'sub',
|
||||
'text': 'Sub',
|
||||
'type': 'agendas',
|
||||
'url': '/api/export-import/agendas/sub/redirect/',
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
# import bundle again, recreate links
|
||||
resp = app.put('/api/export-import/bundle-import/', bundles[0])
|
||||
assert Application.objects.count() == 1
|
||||
assert ApplicationElement.objects.count() == 8
|
||||
|
||||
# no changes since last import
|
||||
resp = app.put('/api/export-import/bundle-check/', bundles[0])
|
||||
assert resp.json == {
|
||||
'data': {
|
||||
'differences': [],
|
||||
'unknown_elements': [],
|
||||
'no_history_elements': [],
|
||||
'legacy_elements': [],
|
||||
}
|
||||
}
|
||||
|
||||
# add local changes
|
||||
snapshots = {}
|
||||
for model in [Agenda, Category, EventsType, Resource, UnavailabilityCalendar]:
|
||||
for instance in model.objects.all():
|
||||
old_snapshot = model.get_snapshot_model().objects.filter(instance=instance).latest('pk')
|
||||
instance.take_snapshot(comment='local changes')
|
||||
new_snapshot = model.get_snapshot_model().objects.filter(instance=instance).latest('pk')
|
||||
assert new_snapshot.pk > old_snapshot.pk
|
||||
snapshots[f'{instance.application_component_type}:{instance.slug}'] = (
|
||||
instance.pk,
|
||||
old_snapshot.pk,
|
||||
new_snapshot.pk,
|
||||
)
|
||||
|
||||
# and check
|
||||
resp = app.put('/api/export-import/bundle-check/', bundles[0])
|
||||
assert resp.json == {
|
||||
'data': {
|
||||
'differences': [
|
||||
{
|
||||
'slug': 'rdv',
|
||||
'type': 'agendas',
|
||||
'url': 'http://testserver/manage/agendas/%s/history/compare/?version1=%s&version2=%s'
|
||||
% (
|
||||
snapshots['agendas:rdv'][0],
|
||||
snapshots['agendas:rdv'][1],
|
||||
snapshots['agendas:rdv'][2],
|
||||
),
|
||||
},
|
||||
{
|
||||
'slug': 'foo',
|
||||
'type': 'agendas_categories',
|
||||
'url': 'http://testserver/manage/category/%s/history/compare/?version1=%s&version2=%s'
|
||||
% (
|
||||
snapshots['agendas_categories:foo'][0],
|
||||
snapshots['agendas_categories:foo'][1],
|
||||
snapshots['agendas_categories:foo'][2],
|
||||
),
|
||||
},
|
||||
{
|
||||
'slug': 'foo',
|
||||
'type': 'resources',
|
||||
'url': 'http://testserver/manage/resource/%s/history/compare/?version1=%s&version2=%s'
|
||||
% (
|
||||
snapshots['resources:foo'][0],
|
||||
snapshots['resources:foo'][1],
|
||||
snapshots['resources:foo'][2],
|
||||
),
|
||||
},
|
||||
{
|
||||
'slug': 'foo',
|
||||
'type': 'unavailability_calendars',
|
||||
'url': 'http://testserver/manage/unavailability-calendar/%s/history/compare/?version1=%s&version2=%s'
|
||||
% (
|
||||
snapshots['unavailability_calendars:foo'][0],
|
||||
snapshots['unavailability_calendars:foo'][1],
|
||||
snapshots['unavailability_calendars:foo'][2],
|
||||
),
|
||||
},
|
||||
{
|
||||
'slug': 'evt',
|
||||
'type': 'agendas',
|
||||
'url': 'http://testserver/manage/agendas/%s/history/compare/?version1=%s&version2=%s'
|
||||
% (
|
||||
snapshots['agendas:evt'][0],
|
||||
snapshots['agendas:evt'][1],
|
||||
snapshots['agendas:evt'][2],
|
||||
),
|
||||
},
|
||||
{
|
||||
'slug': 'foo',
|
||||
'type': 'events_types',
|
||||
'url': 'http://testserver/manage/events-type/%s/history/compare/?version1=%s&version2=%s'
|
||||
% (
|
||||
snapshots['events_types:foo'][0],
|
||||
snapshots['events_types:foo'][1],
|
||||
snapshots['events_types:foo'][2],
|
||||
),
|
||||
},
|
||||
{
|
||||
'slug': 'virt',
|
||||
'type': 'agendas',
|
||||
'url': 'http://testserver/manage/agendas/%s/history/compare/?version1=%s&version2=%s'
|
||||
% (
|
||||
snapshots['agendas:virt'][0],
|
||||
snapshots['agendas:virt'][1],
|
||||
snapshots['agendas:virt'][2],
|
||||
),
|
||||
},
|
||||
{
|
||||
'slug': 'sub',
|
||||
'type': 'agendas',
|
||||
'url': 'http://testserver/manage/agendas/%s/history/compare/?version1=%s&version2=%s'
|
||||
% (
|
||||
snapshots['agendas:sub'][0],
|
||||
snapshots['agendas:sub'][1],
|
||||
snapshots['agendas:sub'][2],
|
||||
),
|
||||
},
|
||||
],
|
||||
'unknown_elements': [],
|
||||
'no_history_elements': [],
|
||||
'legacy_elements': [],
|
||||
}
|
||||
}
|
||||
|
||||
# update bundle
|
||||
resp = app.put('/api/export-import/bundle-import/', bundles[1])
|
||||
|
||||
# and check
|
||||
resp = app.put('/api/export-import/bundle-check/', bundles[1])
|
||||
assert resp.json == {
|
||||
'data': {
|
||||
'differences': [],
|
||||
'unknown_elements': [],
|
||||
'no_history_elements': [],
|
||||
'legacy_elements': [],
|
||||
}
|
||||
}
|
||||
|
||||
# snapshots without application info
|
||||
AgendaSnapshot.objects.update(application_slug=None, application_version=None)
|
||||
resp = app.put('/api/export-import/bundle-check/', bundles[1])
|
||||
assert resp.json == {
|
||||
'data': {
|
||||
'differences': [],
|
||||
'unknown_elements': [],
|
||||
'no_history_elements': [
|
||||
{'slug': 'rdv', 'type': 'agendas'},
|
||||
{'slug': 'evt', 'type': 'agendas'},
|
||||
{'slug': 'virt', 'type': 'agendas'},
|
||||
{'slug': 'sub', 'type': 'agendas'},
|
||||
],
|
||||
'legacy_elements': [],
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ def test_agenda_history(settings, app, admin_user):
|
|||
agenda.description = 'Foo Bar'
|
||||
agenda.save()
|
||||
snapshot2 = agenda.take_snapshot()
|
||||
snapshot2.application_slug = 'foobar'
|
||||
snapshot2.application_version = '42.0'
|
||||
snapshot2.save()
|
||||
assert AgendaSnapshot.objects.count() == 2
|
||||
|
@ -68,6 +69,32 @@ def test_agenda_history(settings, app, admin_user):
|
|||
assert resp.text.count('diff_add') == 16
|
||||
assert resp.text.count('diff_chg') == 0
|
||||
|
||||
# check compare on application version number
|
||||
snapshot1.application_slug = 'foobar'
|
||||
snapshot1.application_version = '41.0'
|
||||
snapshot1.save()
|
||||
# application not found
|
||||
resp = app.get(
|
||||
'/manage/agendas/%s/history/compare/?application=foobaz&version1=41.0&version2=42.0' % agenda.pk
|
||||
)
|
||||
assert resp.location == '/manage/agendas/%s/history/' % agenda.pk
|
||||
# version1 not found
|
||||
resp = app.get(
|
||||
'/manage/agendas/%s/history/compare/?application=foobar&version1=40.0&version2=42.0' % agenda.pk
|
||||
)
|
||||
assert resp.location == '/manage/agendas/%s/history/' % agenda.pk
|
||||
# version2 not found
|
||||
resp = app.get(
|
||||
'/manage/agendas/%s/history/compare/?application=foobar&version1=41.0&version2=43.0' % agenda.pk
|
||||
)
|
||||
assert resp.location == '/manage/agendas/%s/history/' % agenda.pk
|
||||
# ok
|
||||
resp = app.get(
|
||||
'/manage/agendas/%s/history/compare/?application=foobar&version1=41.0&version2=42.0' % agenda.pk
|
||||
)
|
||||
assert 'Snapshot (%s) - (Version 41.0)' % snapshot1.pk in resp
|
||||
assert 'Snapshot (%s) - (Version 42.0)' % snapshot2.pk in resp
|
||||
|
||||
|
||||
def test_agenda_history_as_manager(app, manager_user):
|
||||
agenda = Agenda.objects.create(slug='foo', label='Foo')
|
||||
|
@ -101,6 +128,7 @@ def test_category_history(settings, app, admin_user):
|
|||
category.label = 'Bar'
|
||||
category.save()
|
||||
snapshot2 = category.take_snapshot()
|
||||
snapshot2.application_slug = 'foobar'
|
||||
snapshot2.application_version = '42.0'
|
||||
snapshot2.save()
|
||||
assert CategorySnapshot.objects.count() == 2
|
||||
|
@ -141,6 +169,32 @@ def test_category_history(settings, app, admin_user):
|
|||
assert resp.text.count('diff_add') == 0
|
||||
assert resp.text.count('diff_chg') == 2
|
||||
|
||||
# check compare on application version number
|
||||
snapshot1.application_slug = 'foobar'
|
||||
snapshot1.application_version = '41.0'
|
||||
snapshot1.save()
|
||||
# application not found
|
||||
resp = app.get(
|
||||
'/manage/category/%s/history/compare/?application=foobaz&version1=41.0&version2=42.0' % category.pk
|
||||
)
|
||||
assert resp.location == '/manage/category/%s/history/' % category.pk
|
||||
# version1 not found
|
||||
resp = app.get(
|
||||
'/manage/category/%s/history/compare/?application=foobar&version1=40.0&version2=42.0' % category.pk
|
||||
)
|
||||
assert resp.location == '/manage/category/%s/history/' % category.pk
|
||||
# version2 not found
|
||||
resp = app.get(
|
||||
'/manage/category/%s/history/compare/?application=foobar&version1=41.0&version2=43.0' % category.pk
|
||||
)
|
||||
assert resp.location == '/manage/category/%s/history/' % category.pk
|
||||
# ok
|
||||
resp = app.get(
|
||||
'/manage/category/%s/history/compare/?application=foobar&version1=41.0&version2=42.0' % category.pk
|
||||
)
|
||||
assert 'Snapshot (%s) - (Version 41.0)' % snapshot1.pk in resp
|
||||
assert 'Snapshot (%s) - (Version 42.0)' % snapshot2.pk in resp
|
||||
|
||||
|
||||
def test_events_type_history(settings, app, admin_user):
|
||||
events_type = EventsType.objects.create(slug='foo', label='Foo')
|
||||
|
@ -148,6 +202,7 @@ def test_events_type_history(settings, app, admin_user):
|
|||
events_type.label = 'Bar'
|
||||
events_type.save()
|
||||
snapshot2 = events_type.take_snapshot()
|
||||
snapshot2.application_slug = 'foobar'
|
||||
snapshot2.application_version = '42.0'
|
||||
snapshot2.save()
|
||||
assert EventsTypeSnapshot.objects.count() == 2
|
||||
|
@ -188,6 +243,36 @@ def test_events_type_history(settings, app, admin_user):
|
|||
assert resp.text.count('diff_add') == 0
|
||||
assert resp.text.count('diff_chg') == 2
|
||||
|
||||
# check compare on application version number
|
||||
snapshot1.application_slug = 'foobar'
|
||||
snapshot1.application_version = '41.0'
|
||||
snapshot1.save()
|
||||
# application not found
|
||||
resp = app.get(
|
||||
'/manage/events-type/%s/history/compare/?application=foobaz&version1=41.0&version2=42.0'
|
||||
% events_type.pk
|
||||
)
|
||||
assert resp.location == '/manage/events-type/%s/history/' % events_type.pk
|
||||
# version1 not found
|
||||
resp = app.get(
|
||||
'/manage/events-type/%s/history/compare/?application=foobar&version1=40.0&version2=42.0'
|
||||
% events_type.pk
|
||||
)
|
||||
assert resp.location == '/manage/events-type/%s/history/' % events_type.pk
|
||||
# version2 not found
|
||||
resp = app.get(
|
||||
'/manage/events-type/%s/history/compare/?application=foobar&version1=41.0&version2=43.0'
|
||||
% events_type.pk
|
||||
)
|
||||
assert resp.location == '/manage/events-type/%s/history/' % events_type.pk
|
||||
# ok
|
||||
resp = app.get(
|
||||
'/manage/events-type/%s/history/compare/?application=foobar&version1=41.0&version2=42.0'
|
||||
% events_type.pk
|
||||
)
|
||||
assert 'Snapshot (%s) - (Version 41.0)' % snapshot1.pk in resp
|
||||
assert 'Snapshot (%s) - (Version 42.0)' % snapshot2.pk in resp
|
||||
|
||||
|
||||
def test_resource_history(settings, app, admin_user):
|
||||
resource = Resource.objects.create(slug='foo', label='Foo')
|
||||
|
@ -195,6 +280,7 @@ def test_resource_history(settings, app, admin_user):
|
|||
resource.label = 'Bar'
|
||||
resource.save()
|
||||
snapshot2 = resource.take_snapshot()
|
||||
snapshot2.application_slug = 'foobar'
|
||||
snapshot2.application_version = '42.0'
|
||||
snapshot2.save()
|
||||
assert ResourceSnapshot.objects.count() == 2
|
||||
|
@ -235,6 +321,32 @@ def test_resource_history(settings, app, admin_user):
|
|||
assert resp.text.count('diff_add') == 0
|
||||
assert resp.text.count('diff_chg') == 2
|
||||
|
||||
# check compare on application version number
|
||||
snapshot1.application_slug = 'foobar'
|
||||
snapshot1.application_version = '41.0'
|
||||
snapshot1.save()
|
||||
# application not found
|
||||
resp = app.get(
|
||||
'/manage/resource/%s/history/compare/?application=foobaz&version1=41.0&version2=42.0' % resource.pk
|
||||
)
|
||||
assert resp.location == '/manage/resource/%s/history/' % resource.pk
|
||||
# version1 not found
|
||||
resp = app.get(
|
||||
'/manage/resource/%s/history/compare/?application=foobar&version1=40.0&version2=42.0' % resource.pk
|
||||
)
|
||||
assert resp.location == '/manage/resource/%s/history/' % resource.pk
|
||||
# version2 not found
|
||||
resp = app.get(
|
||||
'/manage/resource/%s/history/compare/?application=foobar&version1=41.0&version2=43.0' % resource.pk
|
||||
)
|
||||
assert resp.location == '/manage/resource/%s/history/' % resource.pk
|
||||
# ok
|
||||
resp = app.get(
|
||||
'/manage/resource/%s/history/compare/?application=foobar&version1=41.0&version2=42.0' % resource.pk
|
||||
)
|
||||
assert 'Snapshot (%s) - (Version 41.0)' % snapshot1.pk in resp
|
||||
assert 'Snapshot (%s) - (Version 42.0)' % snapshot2.pk in resp
|
||||
|
||||
|
||||
def test_unavailability_calendar_history(settings, app, admin_user):
|
||||
unavailability_calendar = UnavailabilityCalendar.objects.create(slug='foo', label='Foo')
|
||||
|
@ -242,6 +354,7 @@ def test_unavailability_calendar_history(settings, app, admin_user):
|
|||
unavailability_calendar.label = 'Bar'
|
||||
unavailability_calendar.save()
|
||||
snapshot2 = unavailability_calendar.take_snapshot()
|
||||
snapshot2.application_slug = 'foobar'
|
||||
snapshot2.application_version = '42.0'
|
||||
snapshot2.save()
|
||||
assert UnavailabilityCalendarSnapshot.objects.count() == 2
|
||||
|
@ -282,6 +395,36 @@ def test_unavailability_calendar_history(settings, app, admin_user):
|
|||
assert resp.text.count('diff_add') == 0
|
||||
assert resp.text.count('diff_chg') == 2
|
||||
|
||||
# check compare on application version number
|
||||
snapshot1.application_slug = 'foobar'
|
||||
snapshot1.application_version = '41.0'
|
||||
snapshot1.save()
|
||||
# application not found
|
||||
resp = app.get(
|
||||
'/manage/unavailability-calendar/%s/history/compare/?application=foobaz&version1=41.0&version2=42.0'
|
||||
% unavailability_calendar.pk
|
||||
)
|
||||
assert resp.location == '/manage/unavailability-calendar/%s/history/' % unavailability_calendar.pk
|
||||
# version1 not found
|
||||
resp = app.get(
|
||||
'/manage/unavailability-calendar/%s/history/compare/?application=foobar&version1=40.0&version2=42.0'
|
||||
% unavailability_calendar.pk
|
||||
)
|
||||
assert resp.location == '/manage/unavailability-calendar/%s/history/' % unavailability_calendar.pk
|
||||
# version2 not found
|
||||
resp = app.get(
|
||||
'/manage/unavailability-calendar/%s/history/compare/?application=foobar&version1=41.0&version2=43.0'
|
||||
% unavailability_calendar.pk
|
||||
)
|
||||
assert resp.location == '/manage/unavailability-calendar/%s/history/' % unavailability_calendar.pk
|
||||
# ok
|
||||
resp = app.get(
|
||||
'/manage/unavailability-calendar/%s/history/compare/?application=foobar&version1=41.0&version2=42.0'
|
||||
% unavailability_calendar.pk
|
||||
)
|
||||
assert 'Snapshot (%s) - (Version 41.0)' % snapshot1.pk in resp
|
||||
assert 'Snapshot (%s) - (Version 42.0)' % snapshot2.pk in resp
|
||||
|
||||
|
||||
def test_unavailability_calendar_history_as_manager(app, manager_user):
|
||||
unavailability_calendar = UnavailabilityCalendar.objects.create(slug='foo', label='Foo')
|
||||
|
|
Loading…
Reference in New Issue
Niveau parcours applification, l’idée de ce component redirect c’est qu’on invite l’utilisateur à lier l’objet à une application existante ? Ou bien juste on redirige vers la page BO de l’objet sans intention particulière ?
C'est pour, depuis hobo, aller visiter l'objet sur la brique qui va bien et vérifier que c'est bien le bon objet. Ce endpoint est appelé par hobo avant la mise à jour d'une application, l'objet peut exister dans la brique mais pas encore dans hobo, où on n'a pas encore les informations nécessaires pour avoir l'url redirect et l'afficher. Donc le endpoint le renvoie par facilité.
Ok, je comprends mieux, merci.