mirror of
https://github.com/ansible/awx.git
synced 2026-03-02 17:28:51 -03:30
filter CredentialType OPTIONS *and* help text for kind requirements
see: #7115 see: #6959
This commit is contained in:
@@ -287,7 +287,13 @@ class GenericAPIView(generics.GenericAPIView, APIView):
|
|||||||
'model_verbose_name': smart_text(self.model._meta.verbose_name),
|
'model_verbose_name': smart_text(self.model._meta.verbose_name),
|
||||||
'model_verbose_name_plural': smart_text(self.model._meta.verbose_name_plural),
|
'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['settings'] = settings
|
||||||
d['has_named_url'] = self.model in settings.NAMED_URL_GRAPH
|
d['has_named_url'] = self.model in settings.NAMED_URL_GRAPH
|
||||||
return d
|
return d
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from rest_framework.relations import RelatedField, ManyRelatedField
|
|||||||
from rest_framework.request import clone_request
|
from rest_framework.request import clone_request
|
||||||
|
|
||||||
# Ansible Tower
|
# Ansible Tower
|
||||||
from awx.main.models import InventorySource, NotificationTemplate, CredentialType
|
from awx.main.models import InventorySource, NotificationTemplate
|
||||||
|
|
||||||
|
|
||||||
class Metadata(metadata.SimpleMetadata):
|
class Metadata(metadata.SimpleMetadata):
|
||||||
@@ -117,6 +117,13 @@ class Metadata(metadata.SimpleMetadata):
|
|||||||
|
|
||||||
return field_info
|
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):
|
def determine_actions(self, request, view):
|
||||||
# Add field information for GET requests (so field names/labels are
|
# Add field information for GET requests (so field names/labels are
|
||||||
# available even when we can't POST/PUT).
|
# 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
|
# If user has appropriate permissions for the view, include
|
||||||
# appropriate metadata about the fields that should be supplied.
|
# appropriate metadata about the fields that should be supplied.
|
||||||
serializer = view.get_serializer(instance=obj)
|
serializer = view.get_serializer(instance=obj)
|
||||||
actions[method] = self.get_serializer_info(serializer)
|
actions[method] = self.get_serializer_info(serializer, method=method)
|
||||||
finally:
|
finally:
|
||||||
view.request = request
|
view.request = request
|
||||||
|
|
||||||
@@ -149,16 +156,6 @@ class Metadata(metadata.SimpleMetadata):
|
|||||||
if field == 'type' and hasattr(serializer, 'get_type_choices'):
|
if field == 'type' and hasattr(serializer, 'get_type_choices'):
|
||||||
meta['choices'] = 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
|
# For GET method, remove meta attributes that aren't relevant
|
||||||
# when reading a field and remove write-only fields.
|
# when reading a field and remove write-only fields.
|
||||||
if method == 'GET':
|
if method == 'GET':
|
||||||
|
|||||||
@@ -287,6 +287,13 @@ class BaseSerializer(serializers.ModelSerializer):
|
|||||||
else:
|
else:
|
||||||
return obj.get_absolute_url(request=self.context.get('request'))
|
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):
|
def _get_related(self, obj):
|
||||||
return {} if obj is None else self.get_related(obj)
|
return {} if obj is None else self.get_related(obj)
|
||||||
|
|
||||||
@@ -1957,6 +1964,16 @@ class CredentialTypeSerializer(BaseSerializer):
|
|||||||
field['help_text'] = _(field['help_text'])
|
field['help_text'] = _(field['help_text'])
|
||||||
return value
|
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
|
# TODO: remove when API v1 is removed
|
||||||
@six.add_metaclass(BaseSerializerMetaclass)
|
@six.add_metaclass(BaseSerializerMetaclass)
|
||||||
|
|||||||
@@ -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 }}:
|
fields to create a new {{ model_verbose_name }}:
|
||||||
|
|
||||||
{% with write_only=1 %}
|
{% with write_only=1 %}
|
||||||
{% include "api/_result_fields_common.md" %}
|
{% include "api/_result_fields_common.md" with serializer_fields=serializer_create_fields %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
{% include "api/_new_in_awx.md" %}
|
{% include "api/_new_in_awx.md" %}
|
||||||
|
|||||||
@@ -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:
|
{{ model_verbose_name }}. The following fields may be modified:
|
||||||
|
|
||||||
{% with write_only=1 %}
|
{% with write_only=1 %}
|
||||||
{% include "api/_result_fields_common.md" %}
|
{% include "api/_result_fields_common.md" with serializer_fields=serializer_update_fields %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
For a PUT request, include **all** fields in the request.
|
For a PUT request, include **all** fields in the request.
|
||||||
|
|||||||
@@ -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:
|
{{ model_verbose_name }}. The following fields may be modified:
|
||||||
|
|
||||||
{% with write_only=1 %}
|
{% with write_only=1 %}
|
||||||
{% include "api/_result_fields_common.md" %}
|
{% include "api/_result_fields_common.md" with serializer_fields=serializer_update_fields %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
For a PUT request, include **all** fields in the request.
|
For a PUT request, include **all** fields in the request.
|
||||||
|
|||||||
@@ -12,6 +12,26 @@ def test_list_as_unauthorized_xfail(get):
|
|||||||
assert response.status_code == 401
|
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
|
@pytest.mark.django_db
|
||||||
def test_list_as_normal_user(get, alice):
|
def test_list_as_normal_user(get, alice):
|
||||||
ssh = CredentialType.defaults['ssh']()
|
ssh = CredentialType.defaults['ssh']()
|
||||||
|
|||||||
Reference in New Issue
Block a user