blob: b39ca22cd719782dcda493eb35c826772d0a2f70 [file] [log] [blame]
Scott Baker3a055582016-03-25 10:55:03 -07001from rest_framework import generics
2from rest_framework import serializers
3from rest_framework.response import Response
4from rest_framework import status
5from xos.apibase import XOSRetrieveUpdateDestroyAPIView, XOSListCreateAPIView
6from rest_framework import viewsets
7from django.conf.urls import patterns, url
Scott Baker0744cf42016-04-08 12:38:54 -07008from xos.exceptions import *
Scott Baker16cd1f02016-05-04 21:14:27 -07009from rest_framework.reverse import reverse
10from django.core.urlresolvers import get_script_prefix, resolve, Resolver404
Scott Baker3a055582016-03-25 10:55:03 -070011
Scott Bakerbdabb9b2016-05-06 16:09:55 -070012# rest_framework 3.x
13ReadOnlyField = serializers.ReadOnlyField
14
15ICON_URLS = {"success": "/static/admin/img/icon_success.gif",
16 "clock": "/static/admin/img/icon_clock.gif",
17 "error": "/static/admin/img/icon_error.gif"}
18
19class PlusObjectMixin:
20 def getBackendIcon(self):
21 (icon, tooltip) = self.get_backend_icon()
22 icon_url = ICON_URLS.get(icon, "unknown")
23 return icon_url
24
25 def getBackendHtml(self):
26 (icon, tooltip) = self.get_backend_icon()
27 icon_url = ICON_URLS.get(icon, "unknown")
28
29 if tooltip:
30 return '<span title="%s"><img src="%s"></span>' % (tooltip, icon_url)
31 else:
32 return '<img src="%s">' % icon_url
Scott Baker3dbdc872016-03-28 13:22:10 -070033
Scott Baker3a055582016-03-25 10:55:03 -070034""" PlusSerializerMixin
35
36 Implements Serializer fields that are common to all OpenCloud objects. For
37 example, stuff related to backend fields.
38"""
39
Scott Baker272ec452016-03-31 16:30:00 -070040class PlusModelSerializer(serializers.ModelSerializer):
Scott Baker3a055582016-03-25 10:55:03 -070041 backendIcon = serializers.SerializerMethodField("getBackendIcon")
42 backendHtml = serializers.SerializerMethodField("getBackendHtml")
43
44 # This will cause a descendant class to pull in the methods defined
45 # above. See rest_framework/serializers.py: _get_declared_fields().
46 base_fields = {"backendIcon": backendIcon, "backendHtml": backendHtml}
47 # Rest_framework 3.0 uses _declared_fields instead of base_fields
48 _declared_fields = {"backendIcon": backendIcon, "backendHtml": backendHtml}
49
50 def getBackendIcon(self, obj):
51 return obj.getBackendIcon()
52
53 def getBackendHtml(self, obj):
54 return obj.getBackendHtml()
55
Scott Baker272ec452016-03-31 16:30:00 -070056 def create(self, validated_data):
57 property_fields = getattr(self, "property_fields", [])
58 create_fields = {}
59 for k in validated_data:
60 if not k in property_fields:
61 create_fields[k] = validated_data[k]
Scott Baker0744cf42016-04-08 12:38:54 -070062 instance = self.Meta.model(**create_fields)
63
Scott Bakerabec39c2016-04-08 12:42:38 -070064 if instance and hasattr(instance,"can_update") and self.context.get('request',None):
65 user = self.context['request'].user
66 if user.__class__.__name__=="AnonymousUser":
67 raise XOSPermissionDenied()
68 if not instance.can_update(user):
69 raise XOSPermissionDenied()
Scott Baker272ec452016-03-31 16:30:00 -070070
71 for k in validated_data:
72 if k in property_fields:
Scott Baker0744cf42016-04-08 12:38:54 -070073 setattr(instance, k, validated_data[k])
Scott Baker272ec452016-03-31 16:30:00 -070074
Scott Baker0744cf42016-04-08 12:38:54 -070075 instance.caller = self.context['request'].user
76 instance.save()
77 return instance
Scott Baker272ec452016-03-31 16:30:00 -070078
79 def update(self, instance, validated_data):
80 nested_fields = getattr(self, "nested_fields", [])
81 for k in validated_data.keys():
82 v = validated_data[k]
83 if k in nested_fields:
84 d = getattr(instance,k)
85 d.update(v)
86 setattr(instance,k,d)
87 else:
88 setattr(instance, k, v)
89 instance.caller = self.context['request'].user
90 instance.save()
91 return instance
92
Scott Baker3a055582016-03-25 10:55:03 -070093class XOSViewSet(viewsets.ModelViewSet):
Scott Baker40fa2302016-03-25 13:33:11 -070094 api_path=""
Scott Baker48dca962016-05-02 17:04:25 -070095 read_only=False
Scott Baker40fa2302016-03-25 13:33:11 -070096
Scott Baker3a055582016-03-25 10:55:03 -070097 @classmethod
Scott Baker2b12c192016-04-01 16:27:53 -070098 def get_api_method_path(self):
99 if self.method_name:
100 return self.api_path + self.method_name + "/"
101 else:
102 return self.api_path
103
104 @classmethod
Scott Baker3a055582016-03-25 10:55:03 -0700105 def detail_url(self, pattern, viewdict, name):
Scott Bakerd9c3a292016-05-02 16:56:09 -0700106 return url(self.get_api_method_path() + r'(?P<pk>[a-zA-Z0-9\-_]+)/' + pattern,
Scott Baker3a055582016-03-25 10:55:03 -0700107 self.as_view(viewdict),
108 name=self.base_name+"_"+name)
109
110 @classmethod
111 def list_url(self, pattern, viewdict, name):
Scott Baker2b12c192016-04-01 16:27:53 -0700112 return url(self.get_api_method_path() + pattern,
Scott Baker3a055582016-03-25 10:55:03 -0700113 self.as_view(viewdict),
114 name=self.base_name+"_"+name)
115
116 @classmethod
Scott Baker3dbdc872016-03-28 13:22:10 -0700117 def get_urlpatterns(self, api_path="^"):
Scott Baker40fa2302016-03-25 13:33:11 -0700118 self.api_path = api_path
119
Scott Baker3a055582016-03-25 10:55:03 -0700120 patterns = []
121
Scott Baker48dca962016-05-02 17:04:25 -0700122 if self.read_only:
123 patterns.append(url(self.get_api_method_path() + '$', self.as_view({'get': 'list'}), name=self.base_name+'_list'))
124 patterns.append(url(self.get_api_method_path() + '(?P<pk>[a-zA-Z0-9\-_]+)/$', self.as_view({'get': 'retrieve'}), name=self.base_name+'_detail'))
125 else:
126 patterns.append(url(self.get_api_method_path() + '$', self.as_view({'get': 'list', 'post': 'create'}), name=self.base_name+'_list'))
127 patterns.append(url(self.get_api_method_path() + '(?P<pk>[a-zA-Z0-9\-_]+)/$', self.as_view({'get': 'retrieve', 'put': 'update', 'post': 'update', 'delete': 'destroy', 'patch': 'partial_update'}), name=self.base_name+'_detail'))
Scott Baker3a055582016-03-25 10:55:03 -0700128
129 return patterns
Scott Bakerc838e882016-04-05 14:13:09 -0700130
Scott Baker6291e492016-04-05 21:27:10 -0700131 def get_serializer_class(self):
132 if hasattr(self, "custom_serializers") and hasattr(self, "action") and (self.action in self.custom_serializers):
133 return self.custom_serializers[self.action]
134 else:
135 return super(XOSViewSet, self).get_serializer_class()
136
Scott Baker0744cf42016-04-08 12:38:54 -0700137 def get_object(self):
138 obj = super(XOSViewSet, self).get_object()
139
140 if self.action=="update" or self.action=="destroy" or self.action.startswith("set_"):
141 if obj and hasattr(obj,"can_update"):
142 user = self.request.user
143 if user.__class__.__name__=="AnonymousUser":
144 raise XOSPermissionDenied()
145 if not obj.can_update(user):
146 raise XOSPermissionDenied()
147
148 return obj
149
Scott Bakerc838e882016-04-05 14:13:09 -0700150class XOSIndexViewSet(viewsets.ViewSet):
151 view_urls=[]
152 subdirs=[]
Scott Baker16cd1f02016-05-04 21:14:27 -0700153 api_path = None
Scott Bakerc838e882016-04-05 14:13:09 -0700154
Scott Baker16cd1f02016-05-04 21:14:27 -0700155 def __init__(self, view_urls, subdirs, api_path):
Scott Bakerc838e882016-04-05 14:13:09 -0700156 self.view_urls = view_urls
157 self.subdirs = subdirs
Scott Baker16cd1f02016-05-04 21:14:27 -0700158 self.api_path = api_path
Scott Bakerc838e882016-04-05 14:13:09 -0700159 super(XOSIndexViewSet, self).__init__()
160
161 def list(self, request):
Scott Baker16cd1f02016-05-04 21:14:27 -0700162 endpoints = {}
Scott Bakerc838e882016-04-05 14:13:09 -0700163 for view_url in self.view_urls:
164 method_name = view_url[1].split("/")[-1]
Scott Baker16cd1f02016-05-04 21:14:27 -0700165 method_url = "http://" + request.get_host() + get_script_prefix() + self.api_path + "/" + method_name
166 endpoints[method_name] = method_url
Scott Bakerc838e882016-04-05 14:13:09 -0700167
168 for subdir in self.subdirs:
Scott Baker16cd1f02016-05-04 21:14:27 -0700169 method_name = subdir
170 method_url = get_script_prefix() + self.api_path + "/" + subdir + "/"
171 # Check to make sure that an endpoint exists at this method_url. This
172 # prunes out subdirs that don't have any methods (like examples/)
173 try:
174 resolve(method_url)
175 except Resolver404:
176 continue
177 method_url = "http://" + request.get_host() + method_url
178 endpoints[method_name] = method_url
Scott Bakerc838e882016-04-05 14:13:09 -0700179
Scott Baker16cd1f02016-05-04 21:14:27 -0700180 return Response(endpoints)
Scott Bakerc838e882016-04-05 14:13:09 -0700181