mirror of
https://github.com/ansible/awx.git
synced 2026-06-18 21:27:43 -02:30
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81b9c1f294 | ||
|
|
c64793d5db |
@@ -961,14 +961,32 @@ class UnifiedJobSerializer(BaseSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class UnifiedJobListSerializer(UnifiedJobSerializer):
|
class UnifiedJobListSerializer(UnifiedJobSerializer):
|
||||||
|
# these fields can be included optionally in the response
|
||||||
|
OPTIONAL_INCLUDE_FIELDS = frozenset({'artifacts', 'extra_vars'})
|
||||||
|
|
||||||
|
# these fields are stripped from the response
|
||||||
|
_STRIPPED_FIELDS = frozenset({'job_args', 'job_cwd', 'job_env', 'result_traceback', 'event_processing_finished', 'artifacts', 'extra_vars'})
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = ('*', '-job_args', '-job_cwd', '-job_env', '-result_traceback', '-event_processing_finished', '-artifacts')
|
fields = ('*', '-job_args', '-job_cwd', '-job_env', '-result_traceback', '-event_processing_finished', '-artifacts', '-extra_vars')
|
||||||
|
|
||||||
|
# processes the include query param if present
|
||||||
|
def _requested_includes(self):
|
||||||
|
request = self.context.get('request')
|
||||||
|
if request is None:
|
||||||
|
return frozenset()
|
||||||
|
raw = request.query_params.get('include', '')
|
||||||
|
requested = {name.strip() for name in raw.split(',') if name.strip()}
|
||||||
|
|
||||||
|
# only allow the fields listed in OPTIONAL_INCLUDE_FIELDS
|
||||||
|
return frozenset(requested) & self.OPTIONAL_INCLUDE_FIELDS
|
||||||
|
|
||||||
def get_field_names(self, declared_fields, info):
|
def get_field_names(self, declared_fields, info):
|
||||||
field_names = super(UnifiedJobListSerializer, self).get_field_names(declared_fields, info)
|
field_names = super(UnifiedJobListSerializer, self).get_field_names(declared_fields, info)
|
||||||
# Meta multiple inheritance and -field_name options don't seem to be
|
# Meta multiple inheritance and -field_name options don't seem to be
|
||||||
# taking effect above, so remove the undesired fields here.
|
# taking effect above, so remove the undesired fields here.
|
||||||
return tuple(x for x in field_names if x not in ('job_args', 'job_cwd', 'job_env', 'result_traceback', 'event_processing_finished', 'artifacts'))
|
strip = self._STRIPPED_FIELDS - self._requested_includes()
|
||||||
|
return tuple(x for x in field_names if x not in strip)
|
||||||
|
|
||||||
def get_types(self):
|
def get_types(self):
|
||||||
if type(self) is UnifiedJobListSerializer:
|
if type(self) is UnifiedJobListSerializer:
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ from awx.api.views.mixin import (
|
|||||||
RelatedJobsPreventDeleteMixin,
|
RelatedJobsPreventDeleteMixin,
|
||||||
UnifiedJobDeletionMixin,
|
UnifiedJobDeletionMixin,
|
||||||
NoTruncateMixin,
|
NoTruncateMixin,
|
||||||
|
UnifiedJobIncludeMixin,
|
||||||
)
|
)
|
||||||
from awx.api.pagination import UnifiedJobEventPagination
|
from awx.api.pagination import UnifiedJobEventPagination
|
||||||
from awx.main.utils import set_environ
|
from awx.main.utils import set_environ
|
||||||
@@ -3850,7 +3851,7 @@ class SystemJobTemplateNotificationTemplatesSuccessList(SystemJobTemplateNotific
|
|||||||
resource_purpose = 'notification templates triggered on system job success'
|
resource_purpose = 'notification templates triggered on system job success'
|
||||||
|
|
||||||
|
|
||||||
class JobList(ListAPIView):
|
class JobList(UnifiedJobIncludeMixin, ListAPIView):
|
||||||
model = models.Job
|
model = models.Job
|
||||||
serializer_class = serializers.JobListSerializer
|
serializer_class = serializers.JobListSerializer
|
||||||
resource_purpose = 'jobs'
|
resource_purpose = 'jobs'
|
||||||
@@ -4567,7 +4568,7 @@ class UnifiedJobTemplateList(ListAPIView):
|
|||||||
resource_purpose = 'unified job templates'
|
resource_purpose = 'unified job templates'
|
||||||
|
|
||||||
|
|
||||||
class UnifiedJobList(ListAPIView):
|
class UnifiedJobList(UnifiedJobIncludeMixin, ListAPIView):
|
||||||
model = models.UnifiedJob
|
model = models.UnifiedJob
|
||||||
serializer_class = serializers.UnifiedJobListSerializer
|
serializer_class = serializers.UnifiedJobListSerializer
|
||||||
search_fields = ('description', 'name', 'job__playbook')
|
search_fields = ('description', 'name', 'job__playbook')
|
||||||
|
|||||||
@@ -212,3 +212,9 @@ class NoTruncateMixin(object):
|
|||||||
if self.request.query_params.get('no_truncate'):
|
if self.request.query_params.get('no_truncate'):
|
||||||
context.update(no_truncate=True)
|
context.update(no_truncate=True)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class UnifiedJobIncludeMixin(object):
|
||||||
|
# Reserve the name 'include' so we can use it as a query param. Otherwise, the rest-filters backend
|
||||||
|
# would treat it as a model field lookup.
|
||||||
|
rest_filters_reserved_names = ('include',)
|
||||||
|
|||||||
@@ -145,3 +145,124 @@ def test_delete_ad_hoc_command_in_active_state(ad_hoc_command_factory, delete, a
|
|||||||
adhoc = ad_hoc_command_factory(initial_state=status)
|
adhoc = ad_hoc_command_factory(initial_state=status)
|
||||||
url = reverse('api:ad_hoc_command_detail', kwargs={'pk': adhoc.pk})
|
url = reverse('api:ad_hoc_command_detail', kwargs={'pk': adhoc.pk})
|
||||||
delete(url, None, admin, expect=403)
|
delete(url, None, admin, expect=403)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def job_with_heavy_fields(job_factory):
|
||||||
|
job = job_factory()
|
||||||
|
job.extra_vars = '{"some_var": "some_value"}'
|
||||||
|
job.artifacts = {"some_artifact": "some_value"}
|
||||||
|
job.save()
|
||||||
|
return job
|
||||||
|
|
||||||
|
|
||||||
|
def _job_result(response, job_id):
|
||||||
|
for row in response.data['results']:
|
||||||
|
if row['id'] == job_id:
|
||||||
|
return row
|
||||||
|
raise AssertionError('job {} not found in {}'.format(job_id, [r['id'] for r in response.data['results']]))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_unified_jobs_list_strips_heavy_fields_by_default(get, admin, job_with_heavy_fields):
|
||||||
|
response = get(reverse('api:unified_job_list') + '?id={}'.format(job_with_heavy_fields.id), admin, expect=200)
|
||||||
|
row = _job_result(response, job_with_heavy_fields.id)
|
||||||
|
assert 'artifacts' not in row
|
||||||
|
assert 'extra_vars' not in row
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_unified_jobs_list_include_artifacts(get, admin, job_with_heavy_fields):
|
||||||
|
response = get(
|
||||||
|
reverse('api:unified_job_list') + '?id={}&include=artifacts'.format(job_with_heavy_fields.id),
|
||||||
|
admin,
|
||||||
|
expect=200,
|
||||||
|
)
|
||||||
|
row = _job_result(response, job_with_heavy_fields.id)
|
||||||
|
assert 'artifacts' in row
|
||||||
|
assert 'extra_vars' not in row
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_unified_jobs_list_include_extra_vars(get, admin, job_with_heavy_fields):
|
||||||
|
response = get(
|
||||||
|
reverse('api:unified_job_list') + '?id={}&include=extra_vars'.format(job_with_heavy_fields.id),
|
||||||
|
admin,
|
||||||
|
expect=200,
|
||||||
|
)
|
||||||
|
row = _job_result(response, job_with_heavy_fields.id)
|
||||||
|
assert 'extra_vars' in row
|
||||||
|
assert 'artifacts' not in row
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_unified_jobs_list_include_both(get, admin, job_with_heavy_fields):
|
||||||
|
response = get(
|
||||||
|
reverse('api:unified_job_list') + '?id={}&include=artifacts,extra_vars'.format(job_with_heavy_fields.id),
|
||||||
|
admin,
|
||||||
|
expect=200,
|
||||||
|
)
|
||||||
|
row = _job_result(response, job_with_heavy_fields.id)
|
||||||
|
assert 'artifacts' in row
|
||||||
|
assert 'extra_vars' in row
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_unified_jobs_list_include_tolerates_whitespace(get, admin, job_with_heavy_fields):
|
||||||
|
response = get(
|
||||||
|
reverse('api:unified_job_list') + '?id={}&include=%20artifacts%20,%20extra_vars%20'.format(job_with_heavy_fields.id),
|
||||||
|
admin,
|
||||||
|
expect=200,
|
||||||
|
)
|
||||||
|
row = _job_result(response, job_with_heavy_fields.id)
|
||||||
|
assert 'artifacts' in row
|
||||||
|
assert 'extra_vars' in row
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_unified_jobs_list_include_ignores_unknown(get, admin, job_with_heavy_fields):
|
||||||
|
response = get(
|
||||||
|
reverse('api:unified_job_list') + '?id={}&include=does_not_exist'.format(job_with_heavy_fields.id),
|
||||||
|
admin,
|
||||||
|
expect=200,
|
||||||
|
)
|
||||||
|
row = _job_result(response, job_with_heavy_fields.id)
|
||||||
|
assert 'artifacts' not in row
|
||||||
|
assert 'extra_vars' not in row
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_unified_jobs_list_include_does_not_honor_disallowed(get, admin, job_with_heavy_fields):
|
||||||
|
# event_processing_finished triggers a count(*) on main_jobevent and must
|
||||||
|
# not be re-enabled via the public ?include= param.
|
||||||
|
response = get(
|
||||||
|
reverse('api:unified_job_list') + '?id={}&include=event_processing_finished,job_args,result_traceback'.format(job_with_heavy_fields.id),
|
||||||
|
admin,
|
||||||
|
expect=200,
|
||||||
|
)
|
||||||
|
row = _job_result(response, job_with_heavy_fields.id)
|
||||||
|
assert 'event_processing_finished' not in row
|
||||||
|
assert 'job_args' not in row
|
||||||
|
assert 'result_traceback' not in row
|
||||||
|
assert 'artifacts' not in row
|
||||||
|
assert 'extra_vars' not in row
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_jobs_list_strips_heavy_fields_by_default(get, admin, job_with_heavy_fields):
|
||||||
|
response = get(reverse('api:job_list') + '?id={}'.format(job_with_heavy_fields.id), admin, expect=200)
|
||||||
|
row = _job_result(response, job_with_heavy_fields.id)
|
||||||
|
assert 'artifacts' not in row
|
||||||
|
assert 'extra_vars' not in row
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_jobs_list_include_extra_vars(get, admin, job_with_heavy_fields):
|
||||||
|
response = get(
|
||||||
|
reverse('api:job_list') + '?id={}&include=extra_vars'.format(job_with_heavy_fields.id),
|
||||||
|
admin,
|
||||||
|
expect=200,
|
||||||
|
)
|
||||||
|
row = _job_result(response, job_with_heavy_fields.id)
|
||||||
|
assert 'extra_vars' in row
|
||||||
|
assert 'artifacts' not in row
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ def test_unified_job_detail_exclusive_fields():
|
|||||||
For each type, assert that the only fields allowed to be exclusive to
|
For each type, assert that the only fields allowed to be exclusive to
|
||||||
detail view are the allowed types
|
detail view are the allowed types
|
||||||
"""
|
"""
|
||||||
allowed_detail_fields = frozenset(('result_traceback', 'job_args', 'job_cwd', 'job_env', 'event_processing_finished', 'artifacts'))
|
allowed_detail_fields = frozenset(('result_traceback', 'job_args', 'job_cwd', 'job_env', 'event_processing_finished', 'artifacts', 'extra_vars'))
|
||||||
for cls in UnifiedJob.__subclasses__():
|
for cls in UnifiedJob.__subclasses__():
|
||||||
list_serializer = getattr(serializers, '{}ListSerializer'.format(cls.__name__))
|
list_serializer = getattr(serializers, '{}ListSerializer'.format(cls.__name__))
|
||||||
detail_serializer = getattr(serializers, '{}Serializer'.format(cls.__name__))
|
detail_serializer = getattr(serializers, '{}Serializer'.format(cls.__name__))
|
||||||
|
|||||||
Reference in New Issue
Block a user