thirdparty/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py
changeset 3031 7678f72140e6
parent 2864 2e0b0af889be
equal deleted inserted replaced
3030:09cae668b536 3031:7678f72140e6
   129   """A stub for calling services on a remote server over HTTP.
   129   """A stub for calling services on a remote server over HTTP.
   130 
   130 
   131   You can use this to stub out any service that the remote server supports.
   131   You can use this to stub out any service that the remote server supports.
   132   """
   132   """
   133 
   133 
   134   def __init__(self, server, path):
   134   def __init__(self, server, path, _test_stub_map=None):
   135     """Constructs a new RemoteStub that communicates with the specified server.
   135     """Constructs a new RemoteStub that communicates with the specified server.
   136 
   136 
   137     Args:
   137     Args:
   138       server: An instance of a subclass of
   138       server: An instance of a subclass of
   139         google.appengine.tools.appengine_rpc.AbstractRpcServer.
   139         google.appengine.tools.appengine_rpc.AbstractRpcServer.
   140       path: The path to the handler this stub should send requests to.
   140       path: The path to the handler this stub should send requests to.
   141     """
   141     """
   142     self._server = server
   142     self._server = server
   143     self._path = path
   143     self._path = path
       
   144     self._test_stub_map = _test_stub_map
   144 
   145 
   145   def _PreHookHandler(self, service, call, request, response):
   146   def _PreHookHandler(self, service, call, request, response):
   146     pass
   147     pass
   147 
   148 
   148   def _PostHookHandler(self, service, call, request, response):
   149   def _PostHookHandler(self, service, call, request, response):
   149     pass
   150     pass
   150 
   151 
   151   def MakeSyncCall(self, service, call, request, response):
   152   def MakeSyncCall(self, service, call, request, response):
   152     self._PreHookHandler(service, call, request, response)
   153     self._PreHookHandler(service, call, request, response)
       
   154     try:
       
   155       test_stub = self._test_stub_map and self._test_stub_map.GetStub(service)
       
   156       if test_stub:
       
   157         test_stub.MakeSyncCall(service, call, request, response)
       
   158       else:
       
   159         self._MakeRealSyncCall(service, call, request, response)
       
   160     finally:
       
   161       self._PostHookHandler(service, call, request, response)
       
   162 
       
   163   def _MakeRealSyncCall(self, service, call, request, response):
   153     request_pb = remote_api_pb.Request()
   164     request_pb = remote_api_pb.Request()
   154     request_pb.set_service_name(service)
   165     request_pb.set_service_name(service)
   155     request_pb.set_method(call)
   166     request_pb.set_method(call)
   156     request_pb.mutable_request().set_contents(request.Encode())
   167     request_pb.mutable_request().set_contents(request.Encode())
   157 
   168 
   158     response_pb = remote_api_pb.Response()
   169     response_pb = remote_api_pb.Response()
   159     encoded_request = request_pb.Encode()
   170     encoded_request = request_pb.Encode()
   160     encoded_response = self._server.Send(self._path, encoded_request)
   171     encoded_response = self._server.Send(self._path, encoded_request)
   161     response_pb.ParseFromString(encoded_response)
   172     response_pb.ParseFromString(encoded_response)
   162 
   173 
   163     try:
   174     if response_pb.has_application_error():
   164       if response_pb.has_application_error():
   175       error_pb = response_pb.application_error()
   165         error_pb = response_pb.application_error()
   176       raise apiproxy_errors.ApplicationError(error_pb.code(),
   166         raise datastore._ToDatastoreError(
   177                                              error_pb.detail())
   167             apiproxy_errors.ApplicationError(error_pb.code(), error_pb.detail()))
   178     elif response_pb.has_exception():
   168       elif response_pb.has_exception():
   179       raise pickle.loads(response_pb.exception().contents())
   169         raise pickle.loads(response_pb.exception().contents())
   180     elif response_pb.has_java_exception():
   170       elif response_pb.has_java_exception():
   181       raise UnknownJavaServerError("An unknown error has occured in the "
   171         raise UnknownJavaServerError("An unknown error has occured in the "
   182                                    "Java remote_api handler for this call.")
   172                                      "Java remote_api handler for this call.")
   183     else:
   173       else:
   184       response.ParseFromString(response_pb.response().contents())
   174         response.ParseFromString(response_pb.response().contents())
       
   175     finally:
       
   176       self._PostHookHandler(service, call, request, response)
       
   177 
   185 
   178   def CreateRPC(self):
   186   def CreateRPC(self):
   179     return apiproxy_rpc.RPC(stub=self)
   187     return apiproxy_rpc.RPC(stub=self)
   180 
   188 
   181 
   189 
   185   A specialised stub is required because there are some datastore operations
   193   A specialised stub is required because there are some datastore operations
   186   that preserve state between calls. This stub makes queries possible.
   194   that preserve state between calls. This stub makes queries possible.
   187   Transactions on the remote datastore are unfortunately still impossible.
   195   Transactions on the remote datastore are unfortunately still impossible.
   188   """
   196   """
   189 
   197 
   190   def __init__(self, server, path):
   198   def __init__(self, server, path, default_result_count=20,
   191     super(RemoteDatastoreStub, self).__init__(server, path)
   199                _test_stub_map=None):
       
   200     """Constructor.
       
   201 
       
   202     Args:
       
   203       server: The server name to connect to.
       
   204       path: The URI path on the server.
       
   205       default_result_count: The number of items to fetch, by default, in a
       
   206         datastore Query or Next operation. This affects the batch size of
       
   207         query iterators.
       
   208     """
       
   209     super(RemoteDatastoreStub, self).__init__(server, path, _test_stub_map)
       
   210     self.default_result_count = default_result_count
   192     self.__queries = {}
   211     self.__queries = {}
   193     self.__transactions = {}
   212     self.__transactions = {}
   194 
   213 
   195     self.__next_local_cursor = 1
   214     self.__next_local_cursor = 1
   196     self.__local_cursor_lock = threading.Lock()
   215     self.__local_cursor_lock = threading.Lock()
   235 
   254 
   236     if query is None:
   255     if query is None:
   237       query_result.set_more_results(False)
   256       query_result.set_more_results(False)
   238       return
   257       return
   239 
   258 
       
   259     if next_request.has_count():
       
   260       result_count = next_request.count()
       
   261     else:
       
   262       result_count = self.default_result_count
       
   263 
   240     request = datastore_pb.Query()
   264     request = datastore_pb.Query()
   241     request.CopyFrom(query)
   265     request.CopyFrom(query)
   242     if request.has_limit():
   266     request.set_count(result_count)
   243       request.set_limit(min(request.limit(), next_request.count()))
       
   244     else:
       
   245       request.set_limit(next_request.count())
       
   246     request.set_count(request.limit())
       
   247 
   267 
   248     super(RemoteDatastoreStub, self).MakeSyncCall(
   268     super(RemoteDatastoreStub, self).MakeSyncCall(
   249         'remote_datastore', 'RunQuery', request, query_result)
   269         'remote_datastore', 'RunQuery', request, query_result)
   250 
   270 
   251     query.set_offset(query.offset() + query_result.result_size())
   271     query.set_offset(query.offset() + query_result.result_size())
   414   def _Dynamic_DeleteIndex(self, index, void):
   434   def _Dynamic_DeleteIndex(self, index, void):
   415     raise apiproxy_errors.CapabilityDisabledError(
   435     raise apiproxy_errors.CapabilityDisabledError(
   416         'The remote datastore does not support index manipulation.')
   436         'The remote datastore does not support index manipulation.')
   417 
   437 
   418 
   438 
       
   439 ALL_SERVICES = set([
       
   440     'capability_service',
       
   441     'datastore_v3',
       
   442     'images',
       
   443     'mail',
       
   444     'memcache',
       
   445     'taskqueue',
       
   446     'urlfetch',
       
   447     'xmpp',
       
   448 ])
       
   449 
       
   450 
   419 def ConfigureRemoteApi(app_id,
   451 def ConfigureRemoteApi(app_id,
   420                        path,
   452                        path,
   421                        auth_func,
   453                        auth_func,
   422                        servername=None,
   454                        servername=None,
   423                        rpc_server_factory=appengine_rpc.HttpRpcServer,
   455                        rpc_server_factory=appengine_rpc.HttpRpcServer,
   424                        rtok=None,
   456                        rtok=None,
   425                        secure=False):
   457                        secure=False,
       
   458                        services=None,
       
   459                        default_auth_domain=None):
   426   """Does necessary setup to allow easy remote access to App Engine APIs.
   460   """Does necessary setup to allow easy remote access to App Engine APIs.
   427 
   461 
   428   Either servername must be provided or app_id must not be None.  If app_id
   462   Either servername must be provided or app_id must not be None.  If app_id
   429   is None and a servername is provided, this function will send a request
   463   is None and a servername is provided, this function will send a request
   430   to the server to retrieve the app_id.
   464   to the server to retrieve the app_id.
   441       <app_id>.appspot.com.
   475       <app_id>.appspot.com.
   442     rpc_server_factory: A factory to construct the rpc server for the datastore.
   476     rpc_server_factory: A factory to construct the rpc server for the datastore.
   443     rtok: The validation token to sent with app_id lookups. If None, a random
   477     rtok: The validation token to sent with app_id lookups. If None, a random
   444       token is used.
   478       token is used.
   445     secure: Use SSL when communicating with the server.
   479     secure: Use SSL when communicating with the server.
       
   480     services: A list of services to set up stubs for. If specified, only those
       
   481       services are configured; by default all supported services are configured.
       
   482     default_auth_domain: The authentication domain to use by default.
   446 
   483 
   447   Raises:
   484   Raises:
   448     urllib2.HTTPError: if app_id is not provided and there is an error while
   485     urllib2.HTTPError: if app_id is not provided and there is an error while
   449       retrieving it.
   486       retrieving it.
   450     ConfigurationError: if there is a error configuring the DatstoreFileStub.
   487     ConfigurationError: if there is a error configuring the DatstoreFileStub.
   471       raise ConfigurationError('Token validation failed during app_id lookup. '
   508       raise ConfigurationError('Token validation failed during app_id lookup. '
   472                                '(sent %s, got %s)' % (repr(rtok),
   509                                '(sent %s, got %s)' % (repr(rtok),
   473                                                       repr(app_info['rtok'])))
   510                                                       repr(app_info['rtok'])))
   474     app_id = app_info['app_id']
   511     app_id = app_info['app_id']
   475 
   512 
       
   513   if services is not None:
       
   514     services = set(services)
       
   515     unsupported = services.difference(ALL_SERVICES)
       
   516     if unsupported:
       
   517       raise ConfigurationError('Unsupported service(s): %s'
       
   518                                % (', '.join(unsupported),))
       
   519   else:
       
   520     services = set(ALL_SERVICES)
       
   521 
   476   os.environ['APPLICATION_ID'] = app_id
   522   os.environ['APPLICATION_ID'] = app_id
       
   523   if default_auth_domain:
       
   524     os.environ['AUTH_DOMAIN'] = default_auth_domain
       
   525   elif 'AUTH_DOMAIN' not in os.environ:
       
   526     os.environ['AUTH_DOMAIN'] = 'gmail.com'
   477   apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap()
   527   apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap()
   478   datastore_stub = RemoteDatastoreStub(server, path)
   528   if 'datastore_v3' in services:
   479   apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', datastore_stub)
   529     services.remove('datastore_v3')
       
   530     datastore_stub = RemoteDatastoreStub(server, path)
       
   531     apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', datastore_stub)
   480   stub = RemoteStub(server, path)
   532   stub = RemoteStub(server, path)
   481   for service in ['capability_service', 'images', 'mail', 'memcache',
   533   for service in services:
   482                   'urlfetch']:
       
   483     apiproxy_stub_map.apiproxy.RegisterStub(service, stub)
   534     apiproxy_stub_map.apiproxy.RegisterStub(service, stub)
   484 
   535 
   485 
   536 
   486 def MaybeInvokeAuthentication():
   537 def MaybeInvokeAuthentication():
   487   """Sends an empty request through to the configured end-point.
   538   """Sends an empty request through to the configured end-point.