40 import google |
40 import google |
41 import logging |
41 import logging |
42 import os |
42 import os |
43 import pickle |
43 import pickle |
44 import sha |
44 import sha |
|
45 import sys |
45 import wsgiref.handlers |
46 import wsgiref.handlers |
46 import yaml |
47 import yaml |
47 |
48 |
48 from google.appengine.api import api_base_pb |
49 from google.appengine.api import api_base_pb |
49 from google.appengine.api import apiproxy_stub |
50 from google.appengine.api import apiproxy_stub |
50 from google.appengine.api import apiproxy_stub_map |
51 from google.appengine.api import apiproxy_stub_map |
51 from google.appengine.api import datastore_errors |
|
52 from google.appengine.api import mail_service_pb |
52 from google.appengine.api import mail_service_pb |
53 from google.appengine.api import urlfetch_service_pb |
53 from google.appengine.api import urlfetch_service_pb |
54 from google.appengine.api import users |
54 from google.appengine.api import users |
55 from google.appengine.api.capabilities import capability_service_pb |
55 from google.appengine.api.capabilities import capability_service_pb |
56 from google.appengine.api.images import images_service_pb |
56 from google.appengine.api.images import images_service_pb |
57 from google.appengine.api.memcache import memcache_service_pb |
57 from google.appengine.api.memcache import memcache_service_pb |
|
58 try: |
|
59 __import__('google.appengine.api.labs.taskqueue.taskqueue_service_pb') |
|
60 taskqueue_service_pb = sys.modules.get( |
|
61 'google.appengine.api.labs.taskqueue.taskqueue_service_pb') |
|
62 except ImportError: |
|
63 from google.appengine.api.taskqueue import taskqueue_service_pb |
|
64 from google.appengine.api.xmpp import xmpp_service_pb |
58 from google.appengine.datastore import datastore_pb |
65 from google.appengine.datastore import datastore_pb |
59 from google.appengine.ext import webapp |
66 from google.appengine.ext import webapp |
60 from google.appengine.ext.remote_api import remote_api_pb |
67 from google.appengine.ext.remote_api import remote_api_pb |
61 from google.appengine.runtime import apiproxy_errors |
68 from google.appengine.runtime import apiproxy_errors |
62 |
69 |
71 |
78 |
72 To work around this, RemoteDatastoreStub provides its own implementation of |
79 To work around this, RemoteDatastoreStub provides its own implementation of |
73 RunQuery that immediately returns the query results. |
80 RunQuery that immediately returns the query results. |
74 """ |
81 """ |
75 |
82 |
|
83 def __init__(self, service='datastore_v3', _test_stub_map=None): |
|
84 """Constructor. |
|
85 |
|
86 Args: |
|
87 service: The name of the service |
|
88 _test_stub_map: An APIProxyStubMap to use for testing purposes. |
|
89 """ |
|
90 super(RemoteDatastoreStub, self).__init__(service) |
|
91 if _test_stub_map: |
|
92 self.__call = _test_stub_map.MakeSyncCall |
|
93 else: |
|
94 self.__call = apiproxy_stub_map.MakeSyncCall |
|
95 |
76 def _Dynamic_RunQuery(self, request, response): |
96 def _Dynamic_RunQuery(self, request, response): |
77 """Handle a RunQuery request. |
97 """Handle a RunQuery request. |
78 |
98 |
79 We handle RunQuery by executing a Query and a Next and returning the result |
99 We handle RunQuery by executing a Query and a Next and returning the result |
80 of the Next request. |
100 of the Next request. |
81 """ |
101 """ |
82 runquery_response = datastore_pb.QueryResult() |
102 runquery_response = datastore_pb.QueryResult() |
83 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'RunQuery', |
103 self.__call('datastore_v3', 'RunQuery', request, runquery_response) |
84 request, runquery_response) |
|
85 if runquery_response.result_size() > 0: |
104 if runquery_response.result_size() > 0: |
86 response.CopyFrom(runquery_response) |
105 response.CopyFrom(runquery_response) |
87 return |
106 return |
88 |
107 |
89 next_request = datastore_pb.NextRequest() |
108 next_request = datastore_pb.NextRequest() |
90 next_request.mutable_cursor().CopyFrom(runquery_response.cursor()) |
109 next_request.mutable_cursor().CopyFrom(runquery_response.cursor()) |
91 next_request.set_count(request.limit()) |
110 next_request.set_count(request.limit()) |
92 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Next', |
111 self.__call('datastore_v3', 'Next', next_request, response) |
93 next_request, response) |
|
94 |
112 |
95 def _Dynamic_Transaction(self, request, response): |
113 def _Dynamic_Transaction(self, request, response): |
96 """Handle a Transaction request. |
114 """Handle a Transaction request. |
97 |
115 |
98 We handle transactions by accumulating Put requests on the client end, as |
116 We handle transactions by accumulating Put requests on the client end, as |
100 Transaction is invoked, which verifies that all the entities in the |
118 Transaction is invoked, which verifies that all the entities in the |
101 precondition list still exist and their hashes match, then performs a |
119 precondition list still exist and their hashes match, then performs a |
102 transaction of its own to make the updates. |
120 transaction of its own to make the updates. |
103 """ |
121 """ |
104 tx = datastore_pb.Transaction() |
122 tx = datastore_pb.Transaction() |
105 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'BeginTransaction', |
123 self.__call('datastore_v3', 'BeginTransaction', api_base_pb.VoidProto(), tx) |
106 api_base_pb.VoidProto(), tx) |
|
107 |
124 |
108 preconditions = request.precondition_list() |
125 preconditions = request.precondition_list() |
109 if preconditions: |
126 if preconditions: |
110 get_request = datastore_pb.GetRequest() |
127 get_request = datastore_pb.GetRequest() |
111 get_request.mutable_transaction().CopyFrom(tx) |
128 get_request.mutable_transaction().CopyFrom(tx) |
112 for precondition in preconditions: |
129 for precondition in preconditions: |
113 key = get_request.add_key() |
130 key = get_request.add_key() |
114 key.CopyFrom(precondition.key()) |
131 key.CopyFrom(precondition.key()) |
115 get_response = datastore_pb.GetResponse() |
132 get_response = datastore_pb.GetResponse() |
116 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Get', get_request, |
133 self.__call('datastore_v3', 'Get', get_request, get_response) |
117 get_response) |
|
118 entities = get_response.entity_list() |
134 entities = get_response.entity_list() |
119 assert len(entities) == request.precondition_size() |
135 assert len(entities) == request.precondition_size() |
120 for precondition, entity in zip(preconditions, entities): |
136 for precondition, entity in zip(preconditions, entities): |
121 if precondition.has_hash() != entity.has_entity(): |
137 if precondition.has_hash() != entity.has_entity(): |
122 raise apiproxy_errors.ApplicationError( |
138 raise apiproxy_errors.ApplicationError( |
130 "Transaction precondition failed.") |
146 "Transaction precondition failed.") |
131 |
147 |
132 if request.has_puts(): |
148 if request.has_puts(): |
133 put_request = request.puts() |
149 put_request = request.puts() |
134 put_request.mutable_transaction().CopyFrom(tx) |
150 put_request.mutable_transaction().CopyFrom(tx) |
135 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Put', |
151 self.__call('datastore_v3', 'Put', put_request, response) |
136 put_request, response) |
|
137 |
152 |
138 if request.has_deletes(): |
153 if request.has_deletes(): |
139 delete_request = request.deletes() |
154 delete_request = request.deletes() |
140 delete_request.mutable_transaction().CopyFrom(tx) |
155 delete_request.mutable_transaction().CopyFrom(tx) |
141 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Delete', |
156 self.__call('datastore_v3', 'Delete', delete_request, |
142 delete_request, api_base_pb.VoidProto()) |
157 api_base_pb.VoidProto()) |
143 |
158 |
144 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Commit', tx, |
159 self.__call('datastore_v3', 'Commit', tx, api_base_pb.VoidProto()) |
145 api_base_pb.VoidProto()) |
|
146 |
160 |
147 def _Dynamic_GetIDs(self, request, response): |
161 def _Dynamic_GetIDs(self, request, response): |
148 """Fetch unique IDs for a set of paths.""" |
162 """Fetch unique IDs for a set of paths.""" |
149 for entity in request.entity_list(): |
163 for entity in request.entity_list(): |
150 assert entity.property_size() == 0 |
164 assert entity.property_size() == 0 |
152 assert entity.entity_group().element_size() == 0 |
166 assert entity.entity_group().element_size() == 0 |
153 lastpart = entity.key().path().element_list()[-1] |
167 lastpart = entity.key().path().element_list()[-1] |
154 assert lastpart.id() == 0 and not lastpart.has_name() |
168 assert lastpart.id() == 0 and not lastpart.has_name() |
155 |
169 |
156 tx = datastore_pb.Transaction() |
170 tx = datastore_pb.Transaction() |
157 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'BeginTransaction', |
171 self.__call('datastore_v3', 'BeginTransaction', api_base_pb.VoidProto(), tx) |
158 api_base_pb.VoidProto(), tx) |
172 |
159 |
173 self.__call('datastore_v3', 'Put', request, response) |
160 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Put', request, response) |
174 |
161 |
175 self.__call('datastore_v3', 'Rollback', tx, api_base_pb.VoidProto()) |
162 apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Rollback', tx, |
|
163 api_base_pb.VoidProto()) |
|
164 |
176 |
165 |
177 |
166 SERVICE_PB_MAP = { |
178 SERVICE_PB_MAP = { |
167 'capability_service': { |
179 'capability_service': { |
168 'IsEnabled': (capability_service_pb.IsEnabledRequest, |
180 'IsEnabled': (capability_service_pb.IsEnabledRequest, |
172 'Get': (datastore_pb.GetRequest, datastore_pb.GetResponse), |
184 'Get': (datastore_pb.GetRequest, datastore_pb.GetResponse), |
173 'Put': (datastore_pb.PutRequest, datastore_pb.PutResponse), |
185 'Put': (datastore_pb.PutRequest, datastore_pb.PutResponse), |
174 'Delete': (datastore_pb.DeleteRequest, datastore_pb.DeleteResponse), |
186 'Delete': (datastore_pb.DeleteRequest, datastore_pb.DeleteResponse), |
175 'Count': (datastore_pb.Query, api_base_pb.Integer64Proto), |
187 'Count': (datastore_pb.Query, api_base_pb.Integer64Proto), |
176 'GetIndices': (api_base_pb.StringProto, datastore_pb.CompositeIndices), |
188 'GetIndices': (api_base_pb.StringProto, datastore_pb.CompositeIndices), |
|
189 'AllocateIds':(datastore_pb.AllocateIdsRequest, |
|
190 datastore_pb.AllocateIdsResponse), |
177 }, |
191 }, |
178 'images': { |
192 'images': { |
179 'Transform': (images_service_pb.ImagesTransformRequest, |
193 'Transform': (images_service_pb.ImagesTransformRequest, |
180 images_service_pb.ImagesTransformResponse), |
194 images_service_pb.ImagesTransformResponse), |
181 'Composite': (images_service_pb.ImagesCompositeRequest, |
195 'Composite': (images_service_pb.ImagesCompositeRequest, |
199 'FlushAll': (memcache_service_pb.MemcacheFlushRequest, |
213 'FlushAll': (memcache_service_pb.MemcacheFlushRequest, |
200 memcache_service_pb.MemcacheFlushResponse), |
214 memcache_service_pb.MemcacheFlushResponse), |
201 'Stats': (memcache_service_pb.MemcacheStatsRequest, |
215 'Stats': (memcache_service_pb.MemcacheStatsRequest, |
202 memcache_service_pb.MemcacheStatsResponse), |
216 memcache_service_pb.MemcacheStatsResponse), |
203 }, |
217 }, |
|
218 'taskqueue': { |
|
219 'Add': (taskqueue_service_pb.TaskQueueAddRequest, |
|
220 taskqueue_service_pb.TaskQueueAddResponse), |
|
221 'UpdateQueue':(taskqueue_service_pb.TaskQueueUpdateQueueRequest, |
|
222 taskqueue_service_pb.TaskQueueUpdateQueueResponse), |
|
223 'FetchQueues':(taskqueue_service_pb.TaskQueueFetchQueuesRequest, |
|
224 taskqueue_service_pb.TaskQueueFetchQueuesResponse), |
|
225 'FetchQueueStats':( |
|
226 taskqueue_service_pb.TaskQueueFetchQueueStatsRequest, |
|
227 taskqueue_service_pb.TaskQueueFetchQueueStatsResponse), |
|
228 }, |
204 'remote_datastore': { |
229 'remote_datastore': { |
205 'RunQuery': (datastore_pb.Query, datastore_pb.QueryResult), |
230 'RunQuery': (datastore_pb.Query, datastore_pb.QueryResult), |
206 'Transaction': (remote_api_pb.TransactionRequest, |
231 'Transaction': (remote_api_pb.TransactionRequest, |
207 datastore_pb.PutResponse), |
232 datastore_pb.PutResponse), |
208 'GetIDs': (remote_api_pb.PutRequest, datastore_pb.PutResponse), |
233 'GetIDs': (remote_api_pb.PutRequest, datastore_pb.PutResponse), |
209 }, |
234 }, |
210 'urlfetch': { |
235 'urlfetch': { |
211 'Fetch': (urlfetch_service_pb.URLFetchRequest, |
236 'Fetch': (urlfetch_service_pb.URLFetchRequest, |
212 urlfetch_service_pb.URLFetchResponse), |
237 urlfetch_service_pb.URLFetchResponse), |
|
238 }, |
|
239 'xmpp': { |
|
240 'GetPresence': (xmpp_service_pb.PresenceRequest, |
|
241 xmpp_service_pb.PresenceResponse), |
|
242 'SendMessage': (xmpp_service_pb.XmppMessageRequest, |
|
243 xmpp_service_pb.XmppMessageResponse), |
|
244 'SendInvite': (xmpp_service_pb.XmppInviteRequest, |
|
245 xmpp_service_pb.XmppInviteResponse), |
213 }, |
246 }, |
214 } |
247 } |
215 |
248 |
216 |
249 |
217 class ApiCallHandler(webapp.RequestHandler): |
250 class ApiCallHandler(webapp.RequestHandler): |
265 self.response.set_status(200) |
298 self.response.set_status(200) |
266 except Exception, e: |
299 except Exception, e: |
267 logging.exception('Exception while handling %s', request) |
300 logging.exception('Exception while handling %s', request) |
268 self.response.set_status(200) |
301 self.response.set_status(200) |
269 response.mutable_exception().set_contents(pickle.dumps(e)) |
302 response.mutable_exception().set_contents(pickle.dumps(e)) |
270 if isinstance(e, datastore_errors.Error): |
303 if isinstance(e, apiproxy_errors.ApplicationError): |
271 application_error = response.mutable_application_error() |
304 application_error = response.mutable_application_error() |
272 application_error.setCode(e.application_error) |
305 application_error.set_code(e.application_error) |
273 application_error.setDetail(e.error_detail) |
306 application_error.set_detail(e.error_detail) |
274 self.response.out.write(response.Encode()) |
307 self.response.out.write(response.Encode()) |
275 |
308 |
276 def ExecuteRequest(self, request): |
309 def ExecuteRequest(self, request): |
277 """Executes an API invocation and returns the response object.""" |
310 """Executes an API invocation and returns the response object.""" |
278 service = request.service_name() |
311 service = request.service_name() |