api: add an API to request jobs status (#43278)
This commit is contained in:
parent
4180a18f50
commit
e2fecd6373
|
@ -0,0 +1,23 @@
|
|||
# passerelle - uniform access to multiple data sources and services
|
||||
# Copyright (C) 2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views import JobDetailView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'jobs/(?P<pk>[\w,-]+)/$', JobDetailView.as_view(), name='api-job'),
|
||||
]
|
|
@ -0,0 +1,48 @@
|
|||
# passerelle - uniform access to multiple data sources and services
|
||||
# Copyright (C) 2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import Http404
|
||||
from django.http import JsonResponse
|
||||
from django.views.generic import DetailView
|
||||
|
||||
from passerelle.base.models import Job
|
||||
from passerelle.utils import is_authorized
|
||||
|
||||
|
||||
class JobDetailView(DetailView):
|
||||
model = Job
|
||||
|
||||
def error(self, message):
|
||||
return JsonResponse({'err': 1, 'err_desc': message})
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
try:
|
||||
job = self.get_object()
|
||||
except Http404 as exc:
|
||||
return self.error(str(exc))
|
||||
if not is_authorized(self.request, job.resource, 'can_access'):
|
||||
raise PermissionDenied
|
||||
data = {
|
||||
'id': job.id,
|
||||
'resource': job.resource.__class__.__name__,
|
||||
'parameters': job.parameters,
|
||||
'status': job.status,
|
||||
'status_details': job.status_details,
|
||||
'update_timestamp': job.update_timestamp,
|
||||
'done_timestamp': job.done_timestamp,
|
||||
}
|
||||
return JsonResponse({'err': 0, 'data': data})
|
|
@ -7,6 +7,7 @@ from django.contrib.auth.decorators import login_required
|
|||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
from django.views.static import serve as static_serve
|
||||
|
||||
from .api.urls import urlpatterns as api_urls
|
||||
from .views import (
|
||||
HomePageView, ManageView, ManageAddView,
|
||||
GenericCreateConnectorView, GenericDeleteConnectorView,
|
||||
|
@ -37,6 +38,7 @@ urlpatterns = [
|
|||
decorated_includes(manager_required, include(access_urlpatterns))),
|
||||
url(r'^manage/',
|
||||
decorated_includes(manager_required, include(import_export_urlpatterns))),
|
||||
url('^api/', include(api_urls)),
|
||||
]
|
||||
|
||||
# add patterns from apps
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
# passerelle - uniform access to multiple data sources and services
|
||||
# Copyright (C) 2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import mock
|
||||
import pytest
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import override_settings
|
||||
|
||||
from passerelle.apps.ovh.models import OVHSMSGateway
|
||||
from passerelle.base.models import ApiUser, AccessRight, Job
|
||||
|
||||
from test_manager import login, simple_user, admin_user
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
API_KEY = '1234'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def connector(db):
|
||||
connector = OVHSMSGateway.objects.create(
|
||||
slug='my_connector',
|
||||
)
|
||||
apiuser = ApiUser.objects.create(username='me', keytype='API', key=API_KEY)
|
||||
obj_type = ContentType.objects.get_for_model(OVHSMSGateway)
|
||||
AccessRight.objects.create(codename='can_access', apiuser=apiuser,
|
||||
resource_type=obj_type, resource_pk=connector.pk)
|
||||
return connector
|
||||
|
||||
|
||||
@mock.patch('passerelle.sms.models.SMSResource.send_job')
|
||||
def test_api_jobs(mocked_send_job, app, connector, simple_user, admin_user):
|
||||
assert Job.objects.count() == 0
|
||||
url = reverse('api-job', kwargs={'pk': 22})
|
||||
|
||||
# no job
|
||||
resp = app.get(url, status=200)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == 'No job found matching the query'
|
||||
|
||||
job = connector.add_job('send_job', my_parameter='my_value')
|
||||
assert Job.objects.count() == 1
|
||||
url = reverse('api-job', kwargs={'pk': job.id})
|
||||
|
||||
# app disabled
|
||||
with override_settings(PASSERELLE_APP_OVH_ENABLED=False):
|
||||
resp = app.get(url, status=403)
|
||||
|
||||
# access without permission
|
||||
resp = app.get(url, status=403)
|
||||
|
||||
# apiuser access
|
||||
resp = app.get(url, params={'apikey': API_KEY}, status=200)
|
||||
assert not resp.json['err']
|
||||
|
||||
# logged user access
|
||||
app = login(app, simple_user.username, password='user')
|
||||
resp = app.get(url, status=403)
|
||||
app = login(app, admin_user.username, password='admin')
|
||||
resp = app.get(url, status=200)
|
||||
assert not resp.json['err']
|
||||
|
||||
# registered job
|
||||
assert resp.json['data']['id'] == job.id
|
||||
assert resp.json['data']['resource'] == 'OVHSMSGateway'
|
||||
assert resp.json['data']['parameters'] == {'my_parameter': 'my_value'}
|
||||
assert resp.json['data']['status'] == 'registered'
|
||||
assert resp.json['data']['done_timestamp'] is None
|
||||
update_timestamp1 = resp.json['data']['update_timestamp']
|
||||
|
||||
# completed job
|
||||
connector.jobs()
|
||||
assert mocked_send_job.call_args == mock.call(my_parameter='my_value')
|
||||
resp = app.get(url, status=200)
|
||||
assert not resp.json['err']
|
||||
assert resp.json['data']['id'] == job.id
|
||||
assert resp.json['data']['status'] == 'completed'
|
||||
assert resp.json['data']['done_timestamp'] is not None
|
||||
resp.json['data']['update_timestamp'] < update_timestamp1
|
||||
|
||||
# failed job
|
||||
job = connector.add_job('send_job')
|
||||
assert Job.objects.count() == 2
|
||||
mocked_send_job.side_effect = Exception('my error message')
|
||||
connector.jobs()
|
||||
url = reverse('api-job', kwargs={'pk': job.id})
|
||||
resp = app.get(url, status=200)
|
||||
assert not resp.json['err']
|
||||
assert resp.json['data']['id'] == job.id
|
||||
assert resp.json['data']['status'] == 'failed'
|
||||
assert resp.json['data']['status_details'] == {'error_summary': 'Exception: my error message'}
|
||||
assert resp.json['data']['done_timestamp'] is not None
|
Loading…
Reference in New Issue