mirror of
https://github.com/ZwareBear/awx.git
synced 2026-03-29 12:13:37 -05:00
* notifications_work: (23 commits) Updates to notification unit tests after @wwitzel3's feedback Fix some notifications issues and write some tests Add notification system documentation Clean up flake8 related issues Fixing up some unicode issues Implement tower ui view url on models Sanity check and force proper types in admin check Proper type for in check Adding migration and base notification type Add a periodic administrative notification Refactor message generator Support notification password field encryption Notification configuration type checking Refactor NotificationTemplate to Notifier Implement irc notification backend Add webhook notification backend Pagerduty and Hipchat backends plus some cleanup Notification serializers, views, and tasks Implement notification serializer and validations Notification endpoints and url expositions ...
168 lines
6.9 KiB
Python
168 lines
6.9 KiB
Python
# Copyright (c) 2016 Ansible, Inc.
|
|
# All Rights Reserved.
|
|
|
|
# Django
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.http import Http404
|
|
from django.utils.encoding import force_text
|
|
|
|
# Django REST Framework
|
|
from rest_framework import exceptions
|
|
from rest_framework import metadata
|
|
from rest_framework import serializers
|
|
from rest_framework.request import clone_request
|
|
|
|
# Ansible Tower
|
|
from awx.main.models import InventorySource, Notifier
|
|
|
|
|
|
class Metadata(metadata.SimpleMetadata):
|
|
|
|
# DRF 3.3 doesn't render choices for read-only fields
|
|
#
|
|
# We want to render choices for read-only fields
|
|
#
|
|
# Note: This works in conjuction with logic in serializers.py that sets
|
|
# field property editable=True before calling DRF build_standard_field()
|
|
# Note: Consider expanding this rendering for more than just choices fields
|
|
def _render_read_only_choices(self, field, field_info):
|
|
if field_info.get('read_only') and hasattr(field, 'choices'):
|
|
field_info['choices'] = [
|
|
{
|
|
'value': choice_value,
|
|
'display_name': force_text(choice_name, strings_only=True)
|
|
}
|
|
for choice_value, choice_name in field.choices.items()
|
|
]
|
|
return field_info
|
|
|
|
def get_field_info(self, field):
|
|
field_info = super(Metadata, self).get_field_info(field)
|
|
if hasattr(field, 'choices') and field.choices:
|
|
field_info = self._render_read_only_choices(field, field_info)
|
|
|
|
# Indicate if a field has a default value.
|
|
# FIXME: Still isn't showing all default values?
|
|
try:
|
|
field_info['default'] = field.get_default()
|
|
except serializers.SkipField:
|
|
pass
|
|
|
|
# Indicate if a field is write-only.
|
|
if getattr(field, 'write_only', False):
|
|
field_info['write_only'] = True
|
|
|
|
# Update choices to be a list of 2-tuples instead of list of dicts with
|
|
# value/display_name.
|
|
if 'choices' in field_info:
|
|
choices = []
|
|
for choice in field_info['choices']:
|
|
if isinstance(choice, dict):
|
|
choices.append((choice.get('value'), choice.get('display_name')))
|
|
else:
|
|
choices.append(choice)
|
|
field_info['choices'] = choices
|
|
|
|
# Special handling of inventory source_region choices that vary based on
|
|
# selected inventory source.
|
|
if field.field_name == 'source_regions':
|
|
for cp in ('azure', 'ec2', 'gce', 'rax'):
|
|
get_regions = getattr(InventorySource, 'get_%s_region_choices' % cp)
|
|
field_info['%s_region_choices' % cp] = get_regions()
|
|
|
|
# Special handling of group_by choices for EC2.
|
|
if field.field_name == 'group_by':
|
|
for cp in ('ec2',):
|
|
get_group_by_choices = getattr(InventorySource, 'get_%s_group_by_choices' % cp)
|
|
field_info['%s_group_by_choices' % cp] = get_group_by_choices()
|
|
|
|
# Special handling of notification configuration where the required properties
|
|
# are conditional on the type selected.
|
|
if field.field_name == 'notification_configuration':
|
|
for (notification_type_name, notification_tr_name, notification_type_class) in Notifier.NOTIFICATION_TYPES:
|
|
field_info[notification_type_name] = notification_type_class.init_parameters
|
|
|
|
# Update type of fields returned...
|
|
if field.field_name == 'type':
|
|
field_info['type'] = 'multiple choice'
|
|
elif field.field_name == 'url':
|
|
field_info['type'] = 'string'
|
|
elif field.field_name in ('related', 'summary_fields'):
|
|
field_info['type'] = 'object'
|
|
elif field.field_name in ('created', 'modified'):
|
|
field_info['type'] = 'datetime'
|
|
|
|
return field_info
|
|
|
|
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).
|
|
actions = {}
|
|
for method in {'GET', 'PUT', 'POST'} & set(view.allowed_methods):
|
|
view.request = clone_request(request, method)
|
|
try:
|
|
# Test global permissions
|
|
if hasattr(view, 'check_permissions'):
|
|
view.check_permissions(view.request)
|
|
# Test object permissions
|
|
if method == 'PUT' and hasattr(view, 'get_object'):
|
|
view.get_object()
|
|
except (exceptions.APIException, PermissionDenied, Http404):
|
|
continue
|
|
else:
|
|
# If user has appropriate permissions for the view, include
|
|
# appropriate metadata about the fields that should be supplied.
|
|
serializer = view.get_serializer()
|
|
actions[method] = self.get_serializer_info(serializer)
|
|
finally:
|
|
view.request = request
|
|
|
|
for field, meta in actions[method].items():
|
|
if not isinstance(meta, dict):
|
|
continue
|
|
|
|
# Add type choices if available from the serializer.
|
|
if field == 'type' and hasattr(serializer, 'get_type_choices'):
|
|
meta['choices'] = serializer.get_type_choices()
|
|
|
|
# For GET method, remove meta attributes that aren't relevant
|
|
# when reading a field and remove write-only fields.
|
|
if method == 'GET':
|
|
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)
|
|
|
|
# For PUT/POST methods, remove read-only fields.
|
|
if method in ('PUT', 'POST'):
|
|
if meta.pop('read_only', False):
|
|
actions[method].pop(field)
|
|
|
|
return actions
|
|
|
|
def determine_metadata(self, request, view):
|
|
metadata = super(Metadata, self).determine_metadata(request, view)
|
|
|
|
# Add version number in which view was added to Tower.
|
|
added_in_version = '1.2'
|
|
for version in ('3.0.0', '2.4.0', '2.3.0', '2.2.0', '2.1.0', '2.0.0', '1.4.8', '1.4.5', '1.4', '1.3'):
|
|
if getattr(view, 'new_in_%s' % version.replace('.', ''), False):
|
|
added_in_version = version
|
|
break
|
|
metadata['added_in_version'] = added_in_version
|
|
|
|
# Add type(s) handled by this view/serializer.
|
|
if hasattr(view, 'get_serializer'):
|
|
serializer = view.get_serializer()
|
|
if hasattr(serializer, 'get_types'):
|
|
metadata['types'] = serializer.get_types()
|
|
|
|
# Add search fields if available from the view.
|
|
if getattr(view, 'search_fields', None):
|
|
metadata['search_fields'] = view.search_fields
|
|
|
|
return metadata
|