This commit is contained in:
parent
62c0b91ac4
commit
6f7acc1489
|
@ -0,0 +1,76 @@
|
|||
# Generated by Django 3.2.18 on 2023-09-22 13:03
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('base', '0030_resourcelog_base_resour_appname_298cbc_idx'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Matrix42',
|
||||
fields=[
|
||||
(
|
||||
'id',
|
||||
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
('title', models.CharField(max_length=50, verbose_name='Title')),
|
||||
('slug', models.SlugField(unique=True, verbose_name='Identifier')),
|
||||
('description', models.TextField(verbose_name='Description')),
|
||||
(
|
||||
'basic_auth_username',
|
||||
models.CharField(
|
||||
blank=True, max_length=128, verbose_name='Basic authentication username'
|
||||
),
|
||||
),
|
||||
(
|
||||
'basic_auth_password',
|
||||
models.CharField(
|
||||
blank=True, max_length=128, verbose_name='Basic authentication password'
|
||||
),
|
||||
),
|
||||
(
|
||||
'client_certificate',
|
||||
models.FileField(
|
||||
blank=True, null=True, upload_to='', verbose_name='TLS client certificate'
|
||||
),
|
||||
),
|
||||
(
|
||||
'trusted_certificate_authorities',
|
||||
models.FileField(blank=True, null=True, upload_to='', verbose_name='TLS trusted CAs'),
|
||||
),
|
||||
(
|
||||
'verify_cert',
|
||||
models.BooleanField(blank=True, default=True, verbose_name='TLS verify certificates'),
|
||||
),
|
||||
(
|
||||
'http_proxy',
|
||||
models.CharField(blank=True, max_length=128, verbose_name='HTTP and HTTPS proxy'),
|
||||
),
|
||||
(
|
||||
'base_url',
|
||||
models.URLField(
|
||||
help_text='Example: https://xxx.m42cloud.com/m42Services/api/',
|
||||
verbose_name='Webservice Base URL',
|
||||
),
|
||||
),
|
||||
('token', models.CharField(max_length=512, verbose_name='Authorization Token')),
|
||||
(
|
||||
'users',
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name='_matrix42_matrix42_users_+',
|
||||
related_query_name='+',
|
||||
to='base.ApiUser',
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Matrix42 Public API',
|
||||
},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,200 @@
|
|||
# passerelle - uniform access to multiple data sources and services
|
||||
# Copyright (C) 2023 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 urllib.parse import urljoin
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from passerelle.base.models import BaseResource, HTTPResource
|
||||
from passerelle.utils.api import endpoint
|
||||
from passerelle.utils.jsonresponse import APIError
|
||||
from passerelle.utils.templates import render_to_string
|
||||
|
||||
DICT_SCHEMA = {
|
||||
'$schema': 'http://json-schema.org/draft-04/schema#',
|
||||
'type': 'object',
|
||||
'additionalProperties': True,
|
||||
'unflatten': True,
|
||||
}
|
||||
|
||||
|
||||
class Matrix42(BaseResource, HTTPResource):
|
||||
category = _('Business Process Connectors')
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Matrix42 Public API')
|
||||
|
||||
base_url = models.URLField(
|
||||
_('Webservice Base URL'), help_text=_('Example: https://xxx.m42cloud.com/m42Services/api/')
|
||||
)
|
||||
token = models.CharField(max_length=512, verbose_name=_('Authorization Token'))
|
||||
|
||||
def get_authorization_headers(self):
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + self.token,
|
||||
}
|
||||
token = self.request('ApiToken/GenerateAccessTokenFromApiToken', headers=headers, method='POST')
|
||||
if 'RawToken' not in token:
|
||||
raise APIError('Matrix42 not returned a RawToken: %s' % token)
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + token['RawToken'],
|
||||
}
|
||||
|
||||
def request(self, uri, params=None, json=None, headers=None, method=None, dict_response=True):
|
||||
if headers is None:
|
||||
headers = self.get_authorization_headers()
|
||||
if method is None:
|
||||
method = 'GET' if json is None else 'POST'
|
||||
url = urljoin(self.base_url, uri)
|
||||
response = self.requests.request(method, url, params=params, json=json, headers=headers)
|
||||
status_code = response.status_code
|
||||
try:
|
||||
response = response.json()
|
||||
except ValueError:
|
||||
raise APIError(
|
||||
'Matrix42 returned %s response with invalid JSON content: %r'
|
||||
% (status_code, response.content)
|
||||
)
|
||||
if dict_response:
|
||||
if not isinstance(response, dict):
|
||||
raise APIError(
|
||||
'Matrix42 returned %s response, not returned a dict: %r' % (status_code, response),
|
||||
data=response,
|
||||
)
|
||||
if isinstance(response, dict) and 'ExceptionName' in response:
|
||||
message = response.get('Message') or '(no message)'
|
||||
raise APIError(
|
||||
'Matrix42 returned %s response, ExceptionName "%s": %s'
|
||||
% (status_code, response['ExceptionName'], message),
|
||||
data=response,
|
||||
)
|
||||
if status_code // 100 != 2:
|
||||
raise APIError('Matrix42 returned status code %s' % status_code, data=response)
|
||||
return response
|
||||
|
||||
@endpoint(
|
||||
name='fragment',
|
||||
description=_('Fragment Query'),
|
||||
display_category=_('Fragments'),
|
||||
parameters={
|
||||
'ddname': {
|
||||
'description': _('Technical name of the Data Definition'),
|
||||
'example_value': 'SPSUserClassBase',
|
||||
},
|
||||
'columns': {
|
||||
'description': _('Columns in the result set, separated by comma'),
|
||||
'example_value': 'ID,[Expression-ObjectID] as EOID,LastName,FirstName,MailAddress',
|
||||
},
|
||||
'template': {
|
||||
'description': _(
|
||||
'Django template for text attribute - if none, use DisplayString|DisplayName|Name'
|
||||
),
|
||||
'example_value': '{{ FirstName }} {{ LastName }} ({{ MailAddress }})',
|
||||
},
|
||||
'id_template': {
|
||||
'description': _('Django template for id attribute - if none, use ID'),
|
||||
'example_value': '{{ ID }}',
|
||||
},
|
||||
'search_column': {
|
||||
'description': _('Column for "q" search'),
|
||||
},
|
||||
'q': {'description': _('Search text in search column')},
|
||||
'id': {'description': _('Get the whole fragment with this ID')},
|
||||
},
|
||||
)
|
||||
def fragment(
|
||||
self,
|
||||
request,
|
||||
ddname,
|
||||
columns=None,
|
||||
template=None,
|
||||
id_template=None,
|
||||
search_column=None,
|
||||
q=None,
|
||||
id=None,
|
||||
):
|
||||
def add_id_and_text(result):
|
||||
if id_template:
|
||||
result['id'] = render_to_string(id_template, result)
|
||||
else:
|
||||
result['id'] = result.get('ID')
|
||||
if template:
|
||||
result['text'] = render_to_string(template, result)
|
||||
else:
|
||||
result['text'] = (
|
||||
result.get('DisplayString') or result.get('DisplayName') or result.get('Name') or ''
|
||||
)
|
||||
|
||||
if id:
|
||||
uri = 'data/fragments/%s/%s' % (ddname, id)
|
||||
result = self.request(uri)
|
||||
add_id_and_text(result)
|
||||
return {'data': [result]}
|
||||
|
||||
if q is not None and not search_column:
|
||||
raise APIError('q needs a search_column parameter', http_status=400)
|
||||
|
||||
uri = urljoin(self.base_url, 'data/fragments/%s/schema-info' % ddname)
|
||||
params = {}
|
||||
if columns:
|
||||
params['columns'] = columns
|
||||
if q is not None:
|
||||
params['where'] = "%s LIKE '%%%s%%'" % (search_column, q.replace("'", "''"))
|
||||
results = self.request(uri, params=params).get('Result') or []
|
||||
for result in results:
|
||||
add_id_and_text(result)
|
||||
return {'data': results}
|
||||
|
||||
@endpoint(
|
||||
name='get-object',
|
||||
description=_('Get an object'),
|
||||
display_category=_('Objects'),
|
||||
methods=['get'],
|
||||
pattern=r'^(?P<ciname>.+)/(?P<object_id>.+)$',
|
||||
example_pattern='SPSActivityTypeTicket/01b02f7d-adb6-49e6-aae3-66251ecbf98e',
|
||||
)
|
||||
def get_object(
|
||||
self,
|
||||
request,
|
||||
ciname,
|
||||
object_id,
|
||||
):
|
||||
uri = urljoin(self.base_url, 'data/objects/%s/%s' % (ciname, object_id))
|
||||
return {'data': self.request(uri)}
|
||||
|
||||
@endpoint(
|
||||
name='create-object',
|
||||
display_category=_('Objects'),
|
||||
methods=['post'],
|
||||
pattern=r'^(?P<ciname>.+)$',
|
||||
example_pattern='SPSActivityTypeTicket',
|
||||
post={
|
||||
'description': _('Create an new object'),
|
||||
'request_body': {'schema': {'application/json': DICT_SCHEMA}},
|
||||
},
|
||||
)
|
||||
def create_object(
|
||||
self,
|
||||
request,
|
||||
ciname,
|
||||
post_data,
|
||||
):
|
||||
uri = urljoin(self.base_url, 'data/objects/%s' % ciname)
|
||||
return {'data': self.request(uri, json=post_data, dict_response=False)}
|
|
@ -163,6 +163,7 @@ INSTALLED_APPS = (
|
|||
'passerelle.apps.jsondatastore',
|
||||
'passerelle.apps.ldap',
|
||||
'passerelle.apps.litteralis',
|
||||
'passerelle.apps.matrix42',
|
||||
'passerelle.apps.mdel',
|
||||
'passerelle.apps.mdel_ddpacs',
|
||||
'passerelle.apps.mobyt',
|
||||
|
|
|
@ -0,0 +1,260 @@
|
|||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from passerelle.apps.matrix42.models import Matrix42
|
||||
from passerelle.base.models import AccessRight, ApiUser
|
||||
from tests.utils import FakedResponse, generic_endpoint_url
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
TOKEN = '{"RawToken": "token2","LifeTime":"2200-09-23T06:39:31.5285469Z"}'
|
||||
USERS = (
|
||||
'{"Result":[{"ID":"a9386c3e-cb7a-ed11-a3bb-000d3aaa0172","DisplayString":"User1, Leo",'
|
||||
'"Expression-TypeCase":"46c86c68-42ae-4089-8398-6e4140fe8658",'
|
||||
'"Expression-TypeID":"46c86c68-42ae-4089-8398-6e4140fe8658"},'
|
||||
'{"ID":"12386c3e-cb7a-ed11-a3bb-00bd3aaa0111","DisplayString":"User2, Blah",'
|
||||
'"Expression-TypeCase":"46c86c68-42ae-4089-8398-6e4140fe8658",'
|
||||
'"Expression-TypeID":"46c86c68-42ae-4089-8398-6e4140fe8658"}],'
|
||||
'"Schema":[{"ColumnName":"ID","ColumnType":"GuidType","Localizable":false},'
|
||||
'{"ColumnName":"DisplayString","ColumnType":"StringType","Localizable":false}]}'
|
||||
)
|
||||
USER = '{"ID":"a9386c3e-cb7a-ed11-a3bb-000d3aaa0172","DisplayString":"User1, Leo"}'
|
||||
OBJECT = '{"ID":"424242","SPSActivityClassBase":{"TicketNumber":"TCK0000153","TimeStamp":"AAAAAAHlWr4="}}'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def matrix42():
|
||||
return Matrix42.objects.create(slug='test', base_url='https://matrix42.example.net/api/', token='token1')
|
||||
|
||||
|
||||
@mock.patch('passerelle.utils.Request.request')
|
||||
def test_matrix42_fragment(mocked_request, app, matrix42):
|
||||
endpoint = generic_endpoint_url('matrix42', 'fragment', slug=matrix42.slug)
|
||||
assert endpoint == '/matrix42/test/fragment'
|
||||
|
||||
params = {'ddname': 'SPSUserClassBase'}
|
||||
mocked_request.side_effect = [
|
||||
FakedResponse(content=TOKEN, status_code=200),
|
||||
FakedResponse(content=USERS, status_code=200),
|
||||
]
|
||||
|
||||
resp = app.get(endpoint, params=params, status=403)
|
||||
assert mocked_request.call_count == 0
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'django.core.exceptions.PermissionDenied'
|
||||
|
||||
# open access
|
||||
api = ApiUser.objects.create(username='all', keytype='', key='')
|
||||
obj_type = ContentType.objects.get_for_model(matrix42)
|
||||
AccessRight.objects.create(
|
||||
codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=matrix42.pk
|
||||
)
|
||||
|
||||
# get all users
|
||||
resp = app.get(endpoint, params=params, status=200)
|
||||
assert mocked_request.call_count == 2
|
||||
get_token, get_users = mocked_request.call_args_list
|
||||
assert get_token[0] == (
|
||||
'POST',
|
||||
'https://matrix42.example.net/api/ApiToken/GenerateAccessTokenFromApiToken',
|
||||
)
|
||||
assert get_token[1]['json'] == get_token[1]['params'] == None
|
||||
assert get_token[1]['headers']['Authorization'] == 'Bearer token1'
|
||||
assert get_users[0] == (
|
||||
'GET',
|
||||
'https://matrix42.example.net/api/data/fragments/SPSUserClassBase/schema-info',
|
||||
)
|
||||
assert get_users[1]['json'] is None
|
||||
assert get_users[1]['params'] == {}
|
||||
assert get_users[1]['headers']['Authorization'] == 'Bearer token2'
|
||||
assert resp.json['err'] == 0
|
||||
assert len(resp.json['data']) == 2
|
||||
assert resp.json['data'][0]['id'] == resp.json['data'][0]['ID'] == 'a9386c3e-cb7a-ed11-a3bb-000d3aaa0172'
|
||||
assert resp.json['data'][0]['text'] == resp.json['data'][0]['DisplayString'] == 'User1, Leo'
|
||||
|
||||
# get all users, with parameters
|
||||
params['id_template'] = 'id:{{ID}}'
|
||||
params['template'] = 'ds:{{DisplayString}}'
|
||||
mocked_request.side_effect = [
|
||||
FakedResponse(content=TOKEN, status_code=200),
|
||||
FakedResponse(content=USERS, status_code=200),
|
||||
]
|
||||
resp = app.get(endpoint, params=params, status=200)
|
||||
assert resp.json['err'] == 0
|
||||
assert len(resp.json['data']) == 2
|
||||
assert resp.json['data'][0]['id'] == 'id:a9386c3e-cb7a-ed11-a3bb-000d3aaa0172'
|
||||
assert resp.json['data'][0]['text'] == 'ds:User1, Leo'
|
||||
|
||||
# search user
|
||||
params['q'] = 'User'
|
||||
resp = app.get(endpoint, params=params, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == 'q needs a search_column parameter'
|
||||
params['search_column'] = 'DisplayString'
|
||||
params['columns'] = 'DisplayString'
|
||||
mocked_request.reset_mock()
|
||||
mocked_request.side_effect = [
|
||||
FakedResponse(content=TOKEN, status_code=200),
|
||||
FakedResponse(content=USERS, status_code=200),
|
||||
]
|
||||
resp = app.get(endpoint, params=params, status=200)
|
||||
_, get_users = mocked_request.call_args_list
|
||||
assert get_users[0] == (
|
||||
'GET',
|
||||
'https://matrix42.example.net/api/data/fragments/SPSUserClassBase/schema-info',
|
||||
)
|
||||
assert get_users[1]['params'] == {'columns': 'DisplayString', 'where': "DisplayString LIKE '%User%'"}
|
||||
assert resp.json['err'] == 0
|
||||
assert len(resp.json['data']) == 2
|
||||
assert resp.json['data'][0]['id'] == 'id:a9386c3e-cb7a-ed11-a3bb-000d3aaa0172'
|
||||
assert resp.json['data'][0]['text'] == 'ds:User1, Leo'
|
||||
|
||||
# get one user
|
||||
del params['q']
|
||||
params['id'] = 'a9386c3e-cb7a-ed11-a3bb-000d3aaa0172'
|
||||
mocked_request.reset_mock()
|
||||
mocked_request.side_effect = [
|
||||
FakedResponse(content=TOKEN, status_code=200),
|
||||
FakedResponse(content=USER, status_code=200),
|
||||
]
|
||||
resp = app.get(endpoint, params=params, status=200)
|
||||
_, get_users = mocked_request.call_args_list
|
||||
assert get_users[0] == (
|
||||
'GET',
|
||||
'https://matrix42.example.net/api/data/fragments/SPSUserClassBase/a9386c3e-cb7a-ed11-a3bb-000d3aaa0172',
|
||||
)
|
||||
assert resp.json['err'] == 0
|
||||
assert len(resp.json['data']) == 1
|
||||
assert resp.json['data'][0]['id'] == 'id:a9386c3e-cb7a-ed11-a3bb-000d3aaa0172'
|
||||
assert resp.json['data'][0]['text'] == 'ds:User1, Leo'
|
||||
|
||||
|
||||
@mock.patch('passerelle.utils.Request.request')
|
||||
def test_matrix42_bad_rawtoken(mocked_request, app, matrix42):
|
||||
endpoint = generic_endpoint_url('matrix42', 'fragment', slug=matrix42.slug)
|
||||
params = {'ddname': 'SPSUserClassBase'}
|
||||
# open access
|
||||
api = ApiUser.objects.create(username='all', keytype='', key='')
|
||||
obj_type = ContentType.objects.get_for_model(matrix42)
|
||||
AccessRight.objects.create(
|
||||
codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=matrix42.pk
|
||||
)
|
||||
|
||||
# no RawToken
|
||||
mocked_request.side_effect = [
|
||||
FakedResponse(content='{}', status_code=200),
|
||||
]
|
||||
resp = app.get(endpoint, params=params, status=200)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'passerelle.utils.jsonresponse.APIError'
|
||||
assert resp.json['err_desc'] == 'Matrix42 not returned a RawToken: {}'
|
||||
|
||||
# bad JSON
|
||||
mocked_request.side_effect = [
|
||||
FakedResponse(content='crashme', status_code=200),
|
||||
]
|
||||
resp = app.get(endpoint, params=params, status=200)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'passerelle.utils.jsonresponse.APIError'
|
||||
assert 'invalid JSON' in resp.json['err_desc']
|
||||
|
||||
# not a dict
|
||||
mocked_request.side_effect = [
|
||||
FakedResponse(content='"crashme"', status_code=200),
|
||||
]
|
||||
resp = app.get(endpoint, params=params, status=200)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'passerelle.utils.jsonresponse.APIError'
|
||||
assert 'not returned a dict' in resp.json['err_desc']
|
||||
|
||||
# Matrix42 error
|
||||
mocked_request.side_effect = [
|
||||
FakedResponse(content='{"ExceptionName":"NotFound","Message":"4o4"}', status_code=404),
|
||||
]
|
||||
resp = app.get(endpoint, params=params, status=200)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'passerelle.utils.jsonresponse.APIError'
|
||||
assert resp.json['err_desc'] == 'Matrix42 returned 404 response, ExceptionName "NotFound": 4o4'
|
||||
mocked_request.side_effect = [
|
||||
FakedResponse(content=TOKEN, status_code=500),
|
||||
]
|
||||
resp = app.get(endpoint, params=params, status=200)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'passerelle.utils.jsonresponse.APIError'
|
||||
assert resp.json['err_desc'] == 'Matrix42 returned status code 500'
|
||||
|
||||
|
||||
@mock.patch('passerelle.utils.Request.request')
|
||||
def test_matrix42_object(mocked_request, app, matrix42):
|
||||
api = ApiUser.objects.create(username='all', keytype='', key='')
|
||||
obj_type = ContentType.objects.get_for_model(matrix42)
|
||||
AccessRight.objects.create(
|
||||
codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=matrix42.pk
|
||||
)
|
||||
|
||||
# create-object
|
||||
mocked_request.side_effect = [
|
||||
FakedResponse(content=TOKEN, status_code=200),
|
||||
FakedResponse(content='"424242"', status_code=200),
|
||||
]
|
||||
|
||||
endpoint = generic_endpoint_url('matrix42', 'create-object', slug=matrix42.slug)
|
||||
endpoint += '/SPSActivityTypeTicket'
|
||||
payload = {
|
||||
'SPSActivityClassBase/Subject': 'incident subject',
|
||||
'SPSActivityClassBase/Category': 'category-id',
|
||||
}
|
||||
resp = app.post_json(endpoint, params=payload, status=200)
|
||||
assert mocked_request.call_count == 2
|
||||
get_token, post_object = mocked_request.call_args_list
|
||||
assert get_token[0] == (
|
||||
'POST',
|
||||
'https://matrix42.example.net/api/ApiToken/GenerateAccessTokenFromApiToken',
|
||||
)
|
||||
assert get_token[1]['json'] == get_token[1]['params'] == None
|
||||
assert get_token[1]['headers']['Authorization'] == 'Bearer token1'
|
||||
assert post_object[0] == (
|
||||
'POST',
|
||||
'https://matrix42.example.net/api/data/objects/SPSActivityTypeTicket',
|
||||
)
|
||||
assert post_object[1]['json'] == {
|
||||
'SPSActivityClassBase': {
|
||||
'Subject': 'incident subject',
|
||||
'Category': 'category-id',
|
||||
}
|
||||
}
|
||||
assert post_object[1]['params'] is None
|
||||
assert post_object[1]['headers']['Authorization'] == 'Bearer token2'
|
||||
assert resp.json['err'] == 0
|
||||
assert resp.json['data'] == '424242'
|
||||
|
||||
# get-object
|
||||
mocked_request.reset_mock()
|
||||
mocked_request.side_effect = [
|
||||
FakedResponse(content=TOKEN, status_code=200),
|
||||
FakedResponse(content=OBJECT, status_code=200),
|
||||
]
|
||||
endpoint = generic_endpoint_url('matrix42', 'get-object', slug=matrix42.slug)
|
||||
endpoint += '/SPSActivityTypeTicket/424242' # ciName + id
|
||||
resp = app.get(endpoint, status=200)
|
||||
assert mocked_request.call_count == 2
|
||||
get_token, get_object = mocked_request.call_args_list
|
||||
assert get_token[0] == (
|
||||
'POST',
|
||||
'https://matrix42.example.net/api/ApiToken/GenerateAccessTokenFromApiToken',
|
||||
)
|
||||
assert get_token[1]['json'] == get_token[1]['params'] == None
|
||||
assert get_token[1]['headers']['Authorization'] == 'Bearer token1'
|
||||
assert get_object[0] == (
|
||||
'GET',
|
||||
'https://matrix42.example.net/api/data/objects/SPSActivityTypeTicket/424242',
|
||||
)
|
||||
assert get_object[1]['json'] == get_object[1]['params'] == None
|
||||
assert get_object[1]['headers']['Authorization'] == 'Bearer token2'
|
||||
assert resp.json['err'] == 0
|
||||
assert resp.json['data'] == {
|
||||
'ID': '424242',
|
||||
'SPSActivityClassBase': {'TicketNumber': 'TCK0000153', 'TimeStamp': 'AAAAAAHlWr4='},
|
||||
}
|
Loading…
Reference in New Issue