1
0
mirror of https://github.com/ansible/awx.git synced 2024-11-01 08:21:15 +03:00

Merge pull request #7150 from ryanpetrello/fix-7115

filter CredentialType OPTIONS *and* help text for `kind` requirements
This commit is contained in:
Ryan Petrello 2017-07-19 09:29:52 -04:00 committed by GitHub
commit 628a0620ac
7 changed files with 56 additions and 16 deletions

View File

@ -287,7 +287,13 @@ class GenericAPIView(generics.GenericAPIView, APIView):
'model_verbose_name': smart_text(self.model._meta.verbose_name),
'model_verbose_name_plural': smart_text(self.model._meta.verbose_name_plural),
})
d['serializer_fields'] = self.metadata_class().get_serializer_info(self.get_serializer())
serializer = self.get_serializer()
for method, key in [
('GET', 'serializer_fields'),
('POST', 'serializer_create_fields'),
('PUT', 'serializer_update_fields')
]:
d[key] = self.metadata_class().get_serializer_info(serializer, method=method)
d['settings'] = settings
d['has_named_url'] = self.model in settings.NAMED_URL_GRAPH
return d

View File

@ -17,7 +17,7 @@ from rest_framework.relations import RelatedField, ManyRelatedField
from rest_framework.request import clone_request
# Ansible Tower
from awx.main.models import InventorySource, NotificationTemplate, CredentialType
from awx.main.models import InventorySource, NotificationTemplate
class Metadata(metadata.SimpleMetadata):
@ -117,6 +117,13 @@ class Metadata(metadata.SimpleMetadata):
return field_info
def get_serializer_info(self, serializer, method=None):
filterer = getattr(serializer, 'filter_field_metadata', lambda fields, method: fields)
return filterer(
super(Metadata, self).get_serializer_info(serializer),
method
)
def determine_actions(self, request, view):
# Add field information for GET requests (so field names/labels are
# available even when we can't POST/PUT).
@ -137,7 +144,7 @@ class Metadata(metadata.SimpleMetadata):
# If user has appropriate permissions for the view, include
# appropriate metadata about the fields that should be supplied.
serializer = view.get_serializer(instance=obj)
actions[method] = self.get_serializer_info(serializer)
actions[method] = self.get_serializer_info(serializer, method=method)
finally:
view.request = request
@ -149,16 +156,6 @@ class Metadata(metadata.SimpleMetadata):
if field == 'type' and hasattr(serializer, 'get_type_choices'):
meta['choices'] = serializer.get_type_choices()
# API-created/modified CredentialType kinds are limited to
# `cloud` and `network`
if method != 'GET' and \
hasattr(serializer, 'Meta') and \
getattr(serializer.Meta, 'model', None) is CredentialType:
actions[method]['kind']['choices'] = filter(
lambda choice: choice[0] in ('cloud', 'net'),
actions[method]['kind']['choices']
)
# For GET method, remove meta attributes that aren't relevant
# when reading a field and remove write-only fields.
if method == 'GET':

View File

@ -287,6 +287,13 @@ class BaseSerializer(serializers.ModelSerializer):
else:
return obj.get_absolute_url(request=self.context.get('request'))
def filter_field_metadata(self, fields, method):
"""
Filter field metadata based on the request method.
This it intended to be extended by subclasses.
"""
return fields
def _get_related(self, obj):
return {} if obj is None else self.get_related(obj)
@ -1957,6 +1964,16 @@ class CredentialTypeSerializer(BaseSerializer):
field['help_text'] = _(field['help_text'])
return value
def filter_field_metadata(self, fields, method):
# API-created/modified CredentialType kinds are limited to
# `cloud` and `net`
if method in ('PUT', 'POST'):
fields['kind']['choices'] = filter(
lambda choice: choice[0] in ('cloud', 'net'),
fields['kind']['choices']
)
return fields
# TODO: remove when API v1 is removed
@six.add_metaclass(BaseSerializerMetaclass)

View File

@ -6,7 +6,7 @@ Make a POST request to this resource with the following {{ model_verbose_name }}
fields to create a new {{ model_verbose_name }}:
{% with write_only=1 %}
{% include "api/_result_fields_common.md" %}
{% include "api/_result_fields_common.md" with serializer_fields=serializer_create_fields %}
{% endwith %}
{% include "api/_new_in_awx.md" %}

View File

@ -15,7 +15,7 @@ Make a PUT or PATCH request to this resource to update this
{{ model_verbose_name }}. The following fields may be modified:
{% with write_only=1 %}
{% include "api/_result_fields_common.md" %}
{% include "api/_result_fields_common.md" with serializer_fields=serializer_update_fields %}
{% endwith %}
For a PUT request, include **all** fields in the request.

View File

@ -15,7 +15,7 @@ Make a PUT or PATCH request to this resource to update this
{{ model_verbose_name }}. The following fields may be modified:
{% with write_only=1 %}
{% include "api/_result_fields_common.md" %}
{% include "api/_result_fields_common.md" with serializer_fields=serializer_update_fields %}
{% endwith %}
For a PUT request, include **all** fields in the request.

View File

@ -12,6 +12,26 @@ def test_list_as_unauthorized_xfail(get):
assert response.status_code == 401
@pytest.mark.django_db
@pytest.mark.parametrize('method, valid', [
('GET', sorted(dict(CredentialType.KIND_CHOICES).keys())),
('POST', ['cloud', 'net']),
])
def test_options_valid_kinds(method, valid, options, admin):
response = options(reverse('api:credential_type_list'), admin)
choices = sorted(dict(response.data['actions'][method]['kind']['choices']).keys())
assert valid == choices
@pytest.mark.django_db
def test_options_valid_put_kinds(options, admin):
ssh = CredentialType.defaults['ssh']()
ssh.save()
response = options(reverse('api:credential_type_detail', kwargs={'pk': ssh.pk}), admin)
choices = sorted(dict(response.data['actions']['PUT']['kind']['choices']).keys())
assert ['cloud', 'net'] == choices
@pytest.mark.django_db
def test_list_as_normal_user(get, alice):
ssh = CredentialType.defaults['ssh']()