Projet

Général

Profil

0001-api-add-api-to-request-jobs-status-43278.patch

Nicolas Roche, 14 août 2020 19:23

Télécharger (8,03 ko)

Voir les différences:

Subject: [PATCH] api: add api to request jobs status (#43278)

 passerelle/api_urls.py  | 23 +++++++++++
 passerelle/api_views.py | 36 +++++++++++++++++
 passerelle/urls.py      |  3 ++
 tests/test_api.py       | 86 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 148 insertions(+)
 create mode 100644 passerelle/api_urls.py
 create mode 100644 passerelle/api_views.py
 create mode 100644 tests/test_api.py
passerelle/api_urls.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2020  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.conf.urls import url
18

  
19
from .api_views import JobView
20

  
21
api_urlpatterns = [
22
    url(r'jobs/(?P<pk>[\w,-]+)/$', JobView.as_view(), name='api-job'),
23
]
passerelle/api_views.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2020  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.http import JsonResponse
18
from django.views.generic import DetailView
19

  
20
from passerelle.base.models import Job
21

  
22
class JobView(DetailView):
23
    model = Job
24

  
25
    def get(self, *args, **kwargs):
26
        job = self.get_object()
27
        data = {
28
            'id': job.id,
29
            'resource': job.resource.__class__.__name__,
30
            'parameters': job.parameters,
31
            'status': job.status,
32
            'status_details': job.status_details,
33
            'update_timestamp': job.update_timestamp,
34
            'done_timestamp': job.done_timestamp,
35
        }
36
        return JsonResponse({'err': 0, 'data': data})
passerelle/urls.py
2 2
from django.conf.urls import include, url
3 3

  
4 4
from django.contrib import admin
5 5

  
6 6
from django.contrib.auth.decorators import login_required
7 7
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
8 8
from django.views.static import serve as static_serve
9 9

  
10
from .api_urls import api_urlpatterns
10 11
from .views import (
11 12
    HomePageView, ManageView, ManageAddView,
12 13
    GenericCreateConnectorView, GenericDeleteConnectorView,
13 14
    GenericEditConnectorView, GenericEndpointView, GenericConnectorView,
14 15
    GenericViewLogsConnectorView, GenericLogView, GenericExportConnectorView,
15 16
    login, logout, menu_json)
16 17
from .base.views import GenericViewJobsConnectorView, GenericJobView, GenericRestartJobView
17 18
from .urls_utils import decorated_includes, manager_required
......
32 33
        'document_root': settings.MEDIA_ROOT,
33 34
    }),
34 35
    url(r'^admin/', admin.site.urls),
35 36

  
36 37
    url(r'^manage/access/',
37 38
        decorated_includes(manager_required, include(access_urlpatterns))),
38 39
    url(r'^manage/',
39 40
        decorated_includes(manager_required, include(import_export_urlpatterns))),
41
    url('^api/',
42
        decorated_includes(manager_required, include(api_urlpatterns))),
40 43
]
41 44

  
42 45
# add patterns from apps
43 46
urlpatterns = register_apps_urls(urlpatterns)
44 47

  
45 48
# add authentication patterns
46 49
urlpatterns += [
47 50
    url(r'^logout/$', logout, name='logout'),
tests/test_api.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2020  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
import mock
18
import pytest
19

  
20
from django.core.urlresolvers import reverse
21

  
22
from passerelle.apps.orange.models import OrangeSMSGateway
23
from passerelle.base.models import Job
24

  
25
from test_manager import login, admin_user
26

  
27

  
28
pytestmark = pytest.mark.django_db
29

  
30

  
31
@pytest.fixture
32
def connector(db):
33
    return OrangeSMSGateway.objects.create(
34
        slug='my_connector',
35
        username='jdoe',
36
        password='secret',
37
        groupname='group2'
38
    )
39

  
40

  
41
@mock.patch('passerelle.sms.models.SMSResource.send_job')
42
def test_api_jobs(mocked_send_job, admin_user, app, connector):
43
    assert Job.objects.count() == 0
44

  
45
    url = reverse('api-job', kwargs={'pk': 1})
46
    resp = app.get(url, status=302)
47
    assert resp.location == '/login/?next=%s' % url
48

  
49
    app = login(app)
50
    assert app.get(url, status=404)
51

  
52
    # registered job
53
    job = connector.add_job('send_job', my_parameter='my_value')
54
    url = reverse('api-job', kwargs={'pk': job.id})
55
    assert Job.objects.count() == 1
56
    resp = app.get(url, status=200)
57
    assert not resp.json['err']
58
    assert resp.json['data']['id'] == job.id
59
    assert resp.json['data']['resource'] == 'OrangeSMSGateway'
60
    assert resp.json['data']['parameters'] == {'my_parameter': 'my_value'}
61
    assert resp.json['data']['status'] == 'registered'
62
    assert resp.json['data']['done_timestamp'] is None
63
    update_timestamp1 = resp.json['data']['update_timestamp']
64

  
65
    # completed job
66
    connector.jobs()
67
    assert mocked_send_job.call_args == mock.call(my_parameter='my_value')
68
    resp = app.get(url, status=200)
69
    assert not resp.json['err']
70
    assert resp.json['data']['id'] == job.id
71
    assert resp.json['data']['status'] == 'completed'
72
    assert resp.json['data']['done_timestamp'] is not None
73
    resp.json['data']['update_timestamp'] < update_timestamp1
74

  
75
    # failed job
76
    job = connector.add_job('send_job')
77
    assert Job.objects.count() == 2
78
    mocked_send_job.side_effect = Exception('my error message')
79
    connector.jobs()
80
    url = reverse('api-job', kwargs={'pk': job.id})
81
    resp = app.get(url, status=200)
82
    assert not resp.json['err']
83
    assert resp.json['data']['id'] == job.id
84
    assert resp.json['data']['status'] == 'failed'
85
    assert resp.json['data']['status_details'] == {'error_summary': 'Exception: my error message'}
86
    assert resp.json['data']['done_timestamp'] is not None
0
-