From 85a9f2f68a960b6fe7013f3460f22b298dde87a8 Mon Sep 17 00:00:00 2001 From: Chris Church Date: Wed, 9 Oct 2013 15:44:19 -0400 Subject: [PATCH] AC-540 Add /api/v1/inventory_sources/ list. OPTIONS request now returns all fields under actions['GET'] and only writable fields under actions['PUT'] or actions['POST']. --- awx/main/base_views.py | 42 ++++++++++++++++++++++++++++++++++++++++++ awx/main/urls.py | 1 + awx/main/views.py | 36 +++++++++++++++++++++--------------- 3 files changed, 64 insertions(+), 15 deletions(-) diff --git a/awx/main/base_views.py b/awx/main/base_views.py index 2a45fa4328..71d354640c 100644 --- a/awx/main/base_views.py +++ b/awx/main/base_views.py @@ -18,6 +18,7 @@ from rest_framework.authentication import get_authorization_header from rest_framework.exceptions import PermissionDenied from rest_framework import generics from rest_framework.response import Response +from rest_framework.request import clone_request from rest_framework import status from rest_framework import views @@ -128,6 +129,47 @@ class GenericAPIView(generics.GenericAPIView, APIView): }) return d + def metadata(self, request): + ''' + Add field information for GET requests (so field names/labels are + available even when we can't POST/PUT). + ''' + ret = super(GenericAPIView, self).metadata(request) + actions = ret.get('actions', {}) + # Remove read only fields from PUT/POST data. + for method in ('POST', 'PUT'): + fields = actions.get(method, {}) + for field, meta in fields.items(): + if not isinstance(meta, dict): + continue + if meta.get('read_only', False): + fields.pop(field) + if 'GET' in self.allowed_methods: + cloned_request = clone_request(request, 'GET') + try: + # Test global permissions + self.check_permissions(cloned_request) + # Test object permissions + if hasattr(self, 'retrieve'): + try: + self.get_object() + except Http404: + # Http404 should be acceptable and the serializer + # metadata should be populated. Except this so the + # outer "else" clause of the try-except-else block + # will be executed. + pass + except (exceptions.APIException, PermissionDenied): + pass + else: + # If user has appropriate permissions for the view, include + # appropriate metadata about the fields that should be supplied. + serializer = self.get_serializer() + actions['GET'] = serializer.metadata() + if actions: + ret['actions'] = actions + return ret + class ListAPIView(generics.ListAPIView, GenericAPIView): # Base class for a read-only list view. diff --git a/awx/main/urls.py b/awx/main/urls.py index 8817a2e702..3efc766fcd 100644 --- a/awx/main/urls.py +++ b/awx/main/urls.py @@ -91,6 +91,7 @@ group_urls = patterns('awx.main.views', ) inventory_source_urls = patterns('awx.main.views', + url(r'^$', 'inventory_source_list'), url(r'^(?P[0-9]+)/$', 'inventory_source_detail'), url(r'^(?P[0-9]+)/update/$', 'inventory_source_update_view'), url(r'^(?P[0-9]+)/inventory_updates/$', 'inventory_source_updates_list'), diff --git a/awx/main/views.py b/awx/main/views.py index 2b39c987c7..5c31438883 100644 --- a/awx/main/views.py +++ b/awx/main/views.py @@ -81,21 +81,21 @@ class ApiV1RootView(APIView): def get(self, request, format=None): ''' list top level resources ''' - data = dict( - organizations = reverse('main:organization_list'), - users = reverse('main:user_list'), - projects = reverse('main:project_list'), - teams = reverse('main:team_list'), - credentials = reverse('main:credential_list'), - inventory = reverse('main:inventory_list'), - groups = reverse('main:group_list'), - hosts = reverse('main:host_list'), - job_templates = reverse('main:job_template_list'), - jobs = reverse('main:job_list'), - authtoken = reverse('main:auth_token_view'), - me = reverse('main:user_me_list'), - config = reverse('main:api_v1_config_view'), - ) + data = SortedDict() + data['authtoken'] = reverse('main:auth_token_view') + data['config'] = reverse('main:api_v1_config_view') + data['me'] = reverse('main:user_me_list') + data['organizations'] = reverse('main:organization_list') + data['users'] = reverse('main:user_list') + data['projects'] = reverse('main:project_list') + data['teams'] = reverse('main:team_list') + data['credentials'] = reverse('main:credential_list') + data['inventory'] = reverse('main:inventory_list') + data['inventory_sources'] = reverse('main:inventory_source_list') + data['groups'] = reverse('main:group_list') + data['hosts'] = reverse('main:host_list') + data['job_templates'] = reverse('main:job_template_list') + data['jobs'] = reverse('main:job_list') return Response(data) class ApiV1ConfigView(APIView): @@ -717,6 +717,12 @@ class InventoryInventorySourcesList(SubListAPIView): return qs.filter(Q(inventory__pk=parent.pk) | Q(group__inventory__pk=parent.pk)) +class InventorySourceList(ListAPIView): + + model = InventorySource + serializer_class = InventorySourceSerializer + new_in_14 = True + class InventorySourceDetail(RetrieveUpdateAPIView): model = InventorySource