mirror of
https://github.com/ansible/awx.git
synced 2026-01-13 02:50:02 -03:30
Merge pull request #198 from cchurch/options_meta_updates
Update field metadata returned in OPTIONS response
This commit is contained in:
commit
693b63ef04
@ -217,7 +217,7 @@ class GenericAPIView(generics.GenericAPIView, APIView):
|
||||
for field, meta in fields.items():
|
||||
if not isinstance(meta, dict):
|
||||
continue
|
||||
if meta.get('read_only', False):
|
||||
if meta.pop('read_only', False):
|
||||
fields.pop(field)
|
||||
if 'GET' in self.allowed_methods:
|
||||
cloned_request = clone_request(request, 'GET')
|
||||
@ -242,12 +242,19 @@ class GenericAPIView(generics.GenericAPIView, APIView):
|
||||
serializer = self.get_serializer()
|
||||
actions['GET'] = serializer.metadata()
|
||||
if hasattr(serializer, 'get_types'):
|
||||
# Inject the type field choices into GET options as well as on
|
||||
# the metadata itself.
|
||||
if 'type' in actions['GET']:
|
||||
actions['GET']['type']['type'] = 'multiple choice'
|
||||
actions['GET']['type']['choices'] = serializer.get_type_choices()
|
||||
ret['types'] = serializer.get_types()
|
||||
# Remove fields labeled as write_only, remove field attributes
|
||||
# that aren't relevant for retrieving data.
|
||||
for field, meta in actions['GET'].items():
|
||||
if not isinstance(meta, dict):
|
||||
continue
|
||||
meta.pop('required', None)
|
||||
meta.pop('read_only', None)
|
||||
meta.pop('default', None)
|
||||
meta.pop('min_length', None)
|
||||
meta.pop('max_length', None)
|
||||
if meta.pop('write_only', False):
|
||||
actions['GET'].pop(field)
|
||||
if actions:
|
||||
ret['actions'] = actions
|
||||
if getattr(self, 'search_fields', None):
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
import functools
|
||||
import json
|
||||
import re
|
||||
import logging
|
||||
@ -86,6 +87,25 @@ SUMMARIZABLE_FK_FIELDS = {
|
||||
'source_script': ('name', 'description'),
|
||||
}
|
||||
|
||||
# Monkeypatch REST framework to include default value and write_only flag in
|
||||
# field metadata.
|
||||
def add_metadata_default(f):
|
||||
@functools.wraps(f)
|
||||
def _add_metadata_default(self, *args, **kwargs):
|
||||
metadata = f(self, *args, **kwargs)
|
||||
if hasattr(self, 'get_default_value'):
|
||||
default = self.get_default_value()
|
||||
if default is None and metadata.get('type', '') != 'field':
|
||||
default = getattr(self, 'empty', None)
|
||||
if default or not getattr(self, 'required', False):
|
||||
metadata['default'] = default
|
||||
if getattr(self, 'write_only', False):
|
||||
metadata['write_only'] = True
|
||||
return metadata
|
||||
return _add_metadata_default
|
||||
|
||||
fields.Field.metadata = add_metadata_default(fields.Field.metadata)
|
||||
|
||||
class ChoiceField(fields.ChoiceField):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -93,7 +113,7 @@ class ChoiceField(fields.ChoiceField):
|
||||
if not self.required:
|
||||
# Remove extra blank option if one is already present (for writable
|
||||
# field) or if present at all for read-only fields.
|
||||
if ([x[0] for x in self.choices].count(u'') > 1 or self.read_only) \
|
||||
if ([x[0] for x in self.choices].count(u'') > 1 or self.get_default_value() != u'' or self.read_only) \
|
||||
and BLANK_CHOICE_DASH[0] in self.choices:
|
||||
self.choices = [x for x in self.choices
|
||||
if x != BLANK_CHOICE_DASH[0]]
|
||||
@ -205,7 +225,7 @@ class BaseSerializer(serializers.ModelSerializer):
|
||||
field.help_text = 'Database ID for this %s.' % unicode(opts.verbose_name)
|
||||
elif key == 'type':
|
||||
field.help_text = 'Data type for this %s.' % unicode(opts.verbose_name)
|
||||
field.type_label = 'string'
|
||||
field.type_label = 'multiple choice'
|
||||
elif key == 'url':
|
||||
field.help_text = 'URL for this %s.' % unicode(opts.verbose_name)
|
||||
field.type_label = 'string'
|
||||
@ -371,6 +391,17 @@ class BaseSerializer(serializers.ModelSerializer):
|
||||
ret.pop(parent_key, None)
|
||||
return ret
|
||||
|
||||
def metadata(self):
|
||||
fields = super(BaseSerializer, self).metadata()
|
||||
for field, meta in fields.items():
|
||||
if not isinstance(meta, dict):
|
||||
continue
|
||||
if field == 'type':
|
||||
meta['choices'] = self.get_type_choices()
|
||||
#if meta.get('type', '') == 'field':
|
||||
# meta['type'] = 'id'
|
||||
return fields
|
||||
|
||||
|
||||
class UnifiedJobTemplateSerializer(BaseSerializer):
|
||||
|
||||
@ -413,8 +444,9 @@ class UnifiedJobTemplateSerializer(BaseSerializer):
|
||||
|
||||
class UnifiedJobSerializer(BaseSerializer):
|
||||
|
||||
result_stdout = serializers.Field(source='result_stdout')
|
||||
unified_job_template = serializers.Field(source='unified_job_template_id')
|
||||
result_stdout = serializers.CharField(source='result_stdout', label='result stdout', read_only=True)
|
||||
unified_job_template = serializers.Field(source='unified_job_template_id', label='unified job template')
|
||||
job_env = serializers.CharField(source='job_env', label='job env', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = UnifiedJob
|
||||
@ -521,9 +553,9 @@ class UnifiedJobStdoutSerializer(UnifiedJobSerializer):
|
||||
|
||||
class UserSerializer(BaseSerializer):
|
||||
|
||||
password = serializers.WritableField(required=False, default='',
|
||||
help_text='Write-only field used to change the password.')
|
||||
ldap_dn = serializers.Field(source='profile.ldap_dn')
|
||||
password = serializers.CharField(required=False, default='', write_only=True,
|
||||
help_text='Write-only field used to change the password.')
|
||||
ldap_dn = serializers.CharField(source='profile.ldap_dn', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
@ -664,10 +696,10 @@ class ProjectOptionsSerializer(BaseSerializer):
|
||||
class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer):
|
||||
|
||||
playbooks = serializers.Field(source='playbooks', help_text='Array of playbooks available within this project.')
|
||||
scm_delete_on_next_update = serializers.Field(source='scm_delete_on_next_update')
|
||||
scm_delete_on_next_update = serializers.BooleanField(source='scm_delete_on_next_update', read_only=True)
|
||||
status = ChoiceField(source='status', choices=Project.PROJECT_STATUS_CHOICES, read_only=True, required=False)
|
||||
last_update_failed = serializers.Field(source='last_update_failed')
|
||||
last_updated = serializers.Field(source='last_updated')
|
||||
last_update_failed = serializers.BooleanField(source='last_update_failed', read_only=True)
|
||||
last_updated = serializers.DateTimeField(source='last_updated', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Project
|
||||
@ -1114,8 +1146,8 @@ class InventorySourceOptionsSerializer(BaseSerializer):
|
||||
class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOptionsSerializer):
|
||||
|
||||
status = ChoiceField(source='status', choices=InventorySource.INVENTORY_SOURCE_STATUS_CHOICES, read_only=True, required=False)
|
||||
last_update_failed = serializers.Field(source='last_update_failed')
|
||||
last_updated = serializers.Field(source='last_updated')
|
||||
last_update_failed = serializers.BooleanField(source='last_update_failed', read_only=True)
|
||||
last_updated = serializers.DateTimeField(source='last_updated', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = InventorySource
|
||||
@ -1273,11 +1305,11 @@ class CredentialSerializer(BaseSerializer):
|
||||
|
||||
# FIXME: may want to make some of these filtered based on user accessing
|
||||
|
||||
password = serializers.WritableField(required=False, default='')
|
||||
ssh_key_data = serializers.WritableField(required=False, default='')
|
||||
ssh_key_unlock = serializers.WritableField(required=False, default='')
|
||||
become_password = serializers.WritableField(required=False, default='')
|
||||
vault_password = serializers.WritableField(required=False, default='')
|
||||
password = serializers.CharField(required=False, default='')
|
||||
ssh_key_data = serializers.CharField(required=False, default='')
|
||||
ssh_key_unlock = serializers.CharField(required=False, default='')
|
||||
become_password = serializers.CharField(required=False, default='')
|
||||
vault_password = serializers.CharField(required=False, default='')
|
||||
|
||||
class Meta:
|
||||
model = Credential
|
||||
@ -1512,6 +1544,7 @@ class JobCancelSerializer(JobSerializer):
|
||||
|
||||
|
||||
class JobRelaunchSerializer(JobSerializer):
|
||||
|
||||
passwords_needed_to_start = serializers.SerializerMethodField('get_passwords_needed_to_start')
|
||||
|
||||
class Meta:
|
||||
@ -1687,8 +1720,8 @@ class JobHostSummarySerializer(BaseSerializer):
|
||||
|
||||
class JobEventSerializer(BaseSerializer):
|
||||
|
||||
event_display = serializers.Field(source='get_event_display2')
|
||||
event_level = serializers.Field(source='event_level')
|
||||
event_display = serializers.CharField(source='get_event_display2', read_only=True)
|
||||
event_level = serializers.IntegerField(source='event_level', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = JobEvent
|
||||
@ -1724,7 +1757,7 @@ class JobEventSerializer(BaseSerializer):
|
||||
|
||||
class AdHocCommandEventSerializer(BaseSerializer):
|
||||
|
||||
event_display = serializers.Field(source='get_event_display')
|
||||
event_display = serializers.CharField(source='get_event_display', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = AdHocCommandEvent
|
||||
@ -1742,8 +1775,9 @@ class AdHocCommandEventSerializer(BaseSerializer):
|
||||
return res
|
||||
|
||||
class JobLaunchSerializer(BaseSerializer):
|
||||
|
||||
passwords_needed_to_start = serializers.Field(source='passwords_needed_to_start')
|
||||
can_start_without_user_input = serializers.Field(source='can_start_without_user_input')
|
||||
can_start_without_user_input = serializers.BooleanField(source='can_start_without_user_input', read_only=True)
|
||||
variables_needed_to_start = serializers.Field(source='variables_needed_to_start')
|
||||
credential_needed_to_start = serializers.SerializerMethodField('get_credential_needed_to_start')
|
||||
survey_enabled = serializers.SerializerMethodField('get_survey_enabled')
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{% for fn, fm in serializer_fields.items %}{% spaceless %}
|
||||
{% if not write_only or not fm.read_only %}
|
||||
* `{{ fn }}`: {{ fm.help_text|capfirst }} ({{ fm.type }}{% if fm.required %}, required{% endif %}{% if fm.read_only %}, read-only{% endif %})
|
||||
{% endif %}
|
||||
* `{{ fn }}`: {{ fm.help_text|capfirst }} ({{ fm.type }}{% if write_only and fm.required %}, required{% endif %}{% if write_only and fm.read_only %}, read-only{% endif %}{% if write_only and not fm.choices and not fm.required %}, default=`{% if fm.type == "string" or fm.type == "email" %}"{% firstof fm.default "" %}"{% else %}{{ fm.default }}{% endif %}`{% endif %}){% if fm.choices %}{% for c in fm.choices %}
|
||||
- `{% if c.0 == "" %}""{% else %}{{ c.0 }}{% endif %}`{% if c.1 != c.0 %}: {{ c.1 }}{% endif %}{% if write_only and c.0 == fm.default %} (default){% endif %}{% endfor %}{% endif %}{% endif %}
|
||||
{% endspaceless %}
|
||||
{% endfor %}
|
||||
|
||||
@ -734,6 +734,7 @@ class InventorySourceOptions(BaseModel):
|
||||
'''
|
||||
|
||||
SOURCE_CHOICES = [
|
||||
('', _('Manual')),
|
||||
('file', _('Local File, Directory or Script')),
|
||||
('rax', _('Rackspace Cloud Servers')),
|
||||
('ec2', _('Amazon EC2')),
|
||||
|
||||
@ -41,6 +41,7 @@ class JobOptions(BaseModel):
|
||||
job_type = models.CharField(
|
||||
max_length=64,
|
||||
choices=JOB_TYPE_CHOICES,
|
||||
default='run',
|
||||
)
|
||||
inventory = models.ForeignKey(
|
||||
'Inventory',
|
||||
|
||||
@ -203,6 +203,7 @@ class Project(UnifiedJobTemplate, ProjectOptions):
|
||||
)
|
||||
scm_update_cache_timeout = models.PositiveIntegerField(
|
||||
default=0,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@ -280,7 +280,7 @@ class JobTemplateTest(BaseJobTestMixin, django.test.TestCase):
|
||||
|
||||
# Test that all required fields are really required.
|
||||
data['name'] = 'another new job template'
|
||||
for field in ('name', 'job_type', 'inventory', 'project', 'playbook'):
|
||||
for field in ('name', 'inventory', 'project', 'playbook'):
|
||||
with self.current_user(self.user_sue):
|
||||
d = dict(data.items())
|
||||
d.pop(field)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user