api: make import/export endpoints generic for other kinds of objects (#82713)
This commit is contained in:
parent
a940ee3961
commit
f5e3f625d2
|
@ -308,6 +308,10 @@ class Agenda(models.Model):
|
|||
validators=[MaxValueValidator(59)],
|
||||
)
|
||||
|
||||
application_component_type = 'agendas'
|
||||
application_label_singular = _('Agenda')
|
||||
application_label_plural = _('Agendas')
|
||||
|
||||
class Meta:
|
||||
ordering = ['label']
|
||||
|
||||
|
|
|
@ -19,9 +19,9 @@ import json
|
|||
import tarfile
|
||||
|
||||
from django.contrib.auth.models import Group
|
||||
from django.http import Http404
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import permissions
|
||||
from rest_framework.generics import GenericAPIView
|
||||
from rest_framework.response import Response
|
||||
|
@ -30,76 +30,104 @@ from chrono.agendas.models import Agenda
|
|||
from chrono.apps.export_import.models import Application, ApplicationElement
|
||||
from chrono.manager.utils import import_site
|
||||
|
||||
klasses = {klass.application_component_type: klass for klass in [Agenda]}
|
||||
|
||||
|
||||
class Index(GenericAPIView):
|
||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
response = [
|
||||
{
|
||||
'id': 'agendas',
|
||||
'text': _('Agendas'),
|
||||
'singular': _('Agenda'),
|
||||
'urls': {
|
||||
'list': request.build_absolute_uri(reverse('api-export-import-agendas-list')),
|
||||
data = []
|
||||
for klass in klasses.values():
|
||||
data.append(
|
||||
{
|
||||
'id': klass.application_component_type,
|
||||
'text': klass.application_label_plural,
|
||||
'singular': klass.application_label_singular,
|
||||
'urls': {
|
||||
'list': request.build_absolute_uri(
|
||||
reverse(
|
||||
'api-export-import-components-list',
|
||||
kwargs={'component_type': klass.application_component_type},
|
||||
)
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
return Response({'data': response})
|
||||
return Response({'data': data})
|
||||
|
||||
|
||||
index = Index.as_view()
|
||||
|
||||
|
||||
def get_agenda_bundle_entry(request, agenda, order):
|
||||
def get_component_bundle_entry(request, component):
|
||||
return {
|
||||
'id': str(agenda.slug),
|
||||
'text': agenda.label,
|
||||
'type': 'agendas',
|
||||
'id': str(component.slug),
|
||||
'text': component.label,
|
||||
'type': component.application_component_type,
|
||||
'urls': {
|
||||
'export': request.build_absolute_uri(
|
||||
reverse('api-export-import-agenda-export', kwargs={'slug': str(agenda.slug)})
|
||||
reverse(
|
||||
'api-export-import-component-export',
|
||||
kwargs={
|
||||
'component_type': component.application_component_type,
|
||||
'slug': str(component.slug),
|
||||
},
|
||||
)
|
||||
),
|
||||
'dependencies': request.build_absolute_uri(
|
||||
reverse('api-export-import-agenda-dependencies', kwargs={'slug': str(agenda.slug)})
|
||||
reverse(
|
||||
'api-export-import-component-dependencies',
|
||||
kwargs={
|
||||
'component_type': component.application_component_type,
|
||||
'slug': str(component.slug),
|
||||
},
|
||||
)
|
||||
),
|
||||
'redirect': request.build_absolute_uri(
|
||||
reverse('api-export-import-agenda-redirect', kwargs={'slug': str(agenda.slug)})
|
||||
reverse(
|
||||
'api-export-import-component-redirect',
|
||||
kwargs={
|
||||
'component_type': component.application_component_type,
|
||||
'slug': str(component.slug),
|
||||
},
|
||||
)
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class ListAgendas(GenericAPIView):
|
||||
class ListComponents(GenericAPIView):
|
||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
response = [
|
||||
get_agenda_bundle_entry(request, x, i) for i, x in enumerate(Agenda.objects.order_by('slug'))
|
||||
]
|
||||
klass = klasses[kwargs['component_type']]
|
||||
response = [get_component_bundle_entry(request, x) for x in klass.objects.order_by('slug')]
|
||||
return Response({'data': response})
|
||||
|
||||
|
||||
list_agendas = ListAgendas.as_view()
|
||||
list_components = ListComponents.as_view()
|
||||
|
||||
|
||||
class ExportAgenda(GenericAPIView):
|
||||
class ExportComponent(GenericAPIView):
|
||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||
|
||||
def get(self, request, slug, *args, **kwargs):
|
||||
serialisation = Agenda.objects.get(slug=slug).export_json()
|
||||
klass = klasses[kwargs['component_type']]
|
||||
serialisation = klass.objects.get(slug=slug).export_json()
|
||||
return Response({'data': serialisation})
|
||||
|
||||
|
||||
export_agenda = ExportAgenda.as_view()
|
||||
export_component = ExportComponent.as_view()
|
||||
|
||||
|
||||
class AgendaDependencies(GenericAPIView):
|
||||
class ComponentDependencies(GenericAPIView):
|
||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||
|
||||
def get(self, request, slug, *args, **kwargs):
|
||||
agenda = Agenda.objects.get(slug=slug)
|
||||
klass = klasses[kwargs['component_type']]
|
||||
component = klass.objects.get(slug=slug)
|
||||
|
||||
def dependency_dict(element):
|
||||
if isinstance(element, Group):
|
||||
|
@ -112,20 +140,21 @@ class AgendaDependencies(GenericAPIView):
|
|||
# for authentic creating its role summary page.
|
||||
'uuid': element.role.uuid if hasattr(element, 'role') else None,
|
||||
}
|
||||
elif isinstance(element, Agenda):
|
||||
return get_agenda_bundle_entry(request, element, 0)
|
||||
return element
|
||||
return get_component_bundle_entry(request, element)
|
||||
|
||||
dependencies = [dependency_dict(x) for x in agenda.get_dependencies() if x]
|
||||
dependencies = [dependency_dict(x) for x in component.get_dependencies() if x]
|
||||
return Response({'err': 0, 'data': dependencies})
|
||||
|
||||
|
||||
agenda_dependencies = AgendaDependencies.as_view()
|
||||
component_dependencies = ComponentDependencies.as_view()
|
||||
|
||||
|
||||
def agenda_redirect(request, slug):
|
||||
agenda = get_object_or_404(Agenda, slug=slug)
|
||||
return redirect(reverse('chrono-manager-agenda-view', kwargs={'pk': agenda.pk}))
|
||||
def component_redirect(request, component_type, slug):
|
||||
klass = klasses[component_type]
|
||||
component = get_object_or_404(klass, slug=slug)
|
||||
if klass == Agenda:
|
||||
return redirect(reverse('chrono-manager-agenda-view', kwargs={'pk': component.pk}))
|
||||
raise Http404
|
||||
|
||||
|
||||
class BundleCheck(GenericAPIView):
|
||||
|
@ -144,7 +173,7 @@ class BundleImport(GenericAPIView):
|
|||
|
||||
def put(self, request, *args, **kwargs):
|
||||
tar_io = io.BytesIO(request.read())
|
||||
agendas = []
|
||||
components = {}
|
||||
with tarfile.open(fileobj=tar_io) as tar:
|
||||
manifest = json.loads(tar.extractfile('manifest.json').read().decode())
|
||||
self.application = Application.update_or_create_from_manifest(
|
||||
|
@ -154,34 +183,41 @@ class BundleImport(GenericAPIView):
|
|||
)
|
||||
|
||||
for element in manifest.get('elements'):
|
||||
if element.get('type') != 'agendas':
|
||||
if element['type'] not in klasses or element['type'] == 'roles':
|
||||
continue
|
||||
agendas.append(
|
||||
json.loads(tar.extractfile(f'agendas/{element["slug"]}').read().decode()).get('data')
|
||||
if element['type'] not in components:
|
||||
components[element['type']] = []
|
||||
component_content = (
|
||||
tar.extractfile('%s/%s' % (element['type'], element['slug'])).read().decode()
|
||||
)
|
||||
components[element['type']].append(json.loads(component_content).get('data'))
|
||||
# init cache of application elements, from manifest
|
||||
self.application_elements = set()
|
||||
# import agendas
|
||||
self.do_something(agendas)
|
||||
self.do_something(components)
|
||||
# create application elements
|
||||
self.link_objects(agendas)
|
||||
self.link_objects(components)
|
||||
# remove obsolete application elements
|
||||
self.unlink_obsolete_objects()
|
||||
return Response({'err': 0})
|
||||
|
||||
def do_something(self, agendas):
|
||||
if agendas:
|
||||
import_site({'agendas': agendas})
|
||||
def do_something(self, components):
|
||||
if components:
|
||||
import_site(components)
|
||||
|
||||
def link_objects(self, agendas):
|
||||
for agenda in agendas:
|
||||
try:
|
||||
existing_agenda = Agenda.objects.get(slug=agenda['slug'])
|
||||
except Agenda.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
element = ApplicationElement.update_or_create_for_object(self.application, existing_agenda)
|
||||
self.application_elements.add(element.content_object)
|
||||
def link_objects(self, components):
|
||||
for component_type, component_list in components.items():
|
||||
klass = klasses[component_type]
|
||||
for component in component_list:
|
||||
try:
|
||||
existing_component = klass.objects.get(slug=component['slug'])
|
||||
except klass.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
element = ApplicationElement.update_or_create_for_object(
|
||||
self.application, existing_component
|
||||
)
|
||||
self.application_elements.add(element.content_object)
|
||||
|
||||
def unlink_obsolete_objects(self):
|
||||
known_elements = ApplicationElement.objects.filter(application=self.application)
|
||||
|
@ -196,7 +232,7 @@ bundle_import = BundleImport.as_view()
|
|||
class BundleDeclare(BundleImport):
|
||||
install = False
|
||||
|
||||
def do_something(self, agendas):
|
||||
def do_something(self, components):
|
||||
# no installation on declare
|
||||
pass
|
||||
|
||||
|
|
|
@ -20,24 +20,28 @@ from . import api_views
|
|||
|
||||
urlpatterns = [
|
||||
path('export-import/', api_views.index, name='api-export-import'),
|
||||
path('export-import/agendas/', api_views.list_agendas, name='api-export-import-agendas-list'),
|
||||
path(
|
||||
'export-import/agendas/<slug:slug>/',
|
||||
api_views.export_agenda,
|
||||
name='api-export-import-agenda-export',
|
||||
),
|
||||
path(
|
||||
'export-import/agendas/<slug:slug>/dependencies/',
|
||||
api_views.agenda_dependencies,
|
||||
name='api-export-import-agenda-dependencies',
|
||||
),
|
||||
path(
|
||||
'export-import/agendas/<slug:slug>/redirect/',
|
||||
api_views.agenda_redirect,
|
||||
name='api-export-import-agenda-redirect',
|
||||
),
|
||||
path('export-import/bundle-check/', api_views.bundle_check),
|
||||
path('export-import/bundle-declare/', api_views.bundle_declare),
|
||||
path('export-import/bundle-import/', api_views.bundle_import),
|
||||
path('export-import/unlink/', api_views.bundle_unlink),
|
||||
path(
|
||||
'export-import/<slug:component_type>/',
|
||||
api_views.list_components,
|
||||
name='api-export-import-components-list',
|
||||
),
|
||||
path(
|
||||
'export-import/<slug:component_type>/<slug:slug>/',
|
||||
api_views.export_component,
|
||||
name='api-export-import-component-export',
|
||||
),
|
||||
path(
|
||||
'export-import/<slug:component_type>/<slug:slug>/dependencies/',
|
||||
api_views.component_dependencies,
|
||||
name='api-export-import-component-dependencies',
|
||||
),
|
||||
path(
|
||||
'export-import/<slug:component_type>/<slug:slug>/redirect/',
|
||||
api_views.component_redirect,
|
||||
name='api-export-import-component-redirect',
|
||||
),
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue