def test_keyword_arguments(self): response = self._post_json('/test_view_two_default_arguments', {'args': [], 'kwargs': {}}) self._assert_api_status_code(response, 200, '/test_view_two_default_arguments with no arguments should result in HTTP 200.') self.assertEqual(jinx_json.loads(response.content), ["default1", "default2"]) response = self._post_json('/test_view_two_default_arguments', {'args': ['test1'], 'kwargs': {}}) self._assert_api_status_code(response, 200, '/test_view_two_default_arguments with no arguments should result in HTTP 200.') self.assertEqual(jinx_json.loads(response.content), ["test1", "default2"]) response = self._post_json('/test_view_two_default_arguments', {'args': [], 'kwargs': {'arg2': 'test2'}}) self._assert_api_status_code(response, 200, '/test_view_two_default_arguments with no arguments should result in HTTP 200.') self.assertEqual(jinx_json.loads(response.content), ["default1", "test2"])
def test_normal_call(self): response = self._post_json("/test_view_normal", []) self.assertEqual(jinx_json.loads(response.content), "Hello, world!", 'test_view_normal should return "Hello, world!"') self._assert_api_status_code(response, 200, 'test_view_normal should return status code 200.')
def do_api_call(self, *args, **kwargs): """Performs the API call for this test case and returns the results. JSON encoding and decoding is handled behind the scenes. The decoded response data is stored in response.data (as opposed to response.content, which will hold the raw JSON blob). """ response = self.client.post( self.api_call_path, jinx_json.dumps(dict(args=args, kwargs=kwargs)), "application/json" ) if response.status_code == 200: self.assertEqual( response["Content-Type"], "application/json", "API call %s(%s) returned type %s instead of application/json" % (self.api_call_path, str(args), response["Content-Type"]), ) response.data = jinx_json.loads(response.content) else: response.data = response.content return response
def test_datetime(self): now = datetime.datetime.now() response = self._post_json('/test_view_echo', [now]) self._assert_api_status_code(response, 200, '/test_view_echo should return HTTP 200.') self.assertEqual(jinx_json.loads(response.content), [now], "should be able to send and receive datetime.datetime objects") delta = datetime.timedelta(seconds=1394875, days=21) response = self._post_json('/test_view_echo', [delta]) self._assert_api_status_code(response, 200, '/test_view_echo should return HTTP 200.') self.assertEqual(jinx_json.loads(response.content), [delta], "should be able to send and receive datetime.timedelta objects") response = self._post_json('/test_view_echo', [[[[[[{'1': now}]]], {'2': delta}]]]) self._assert_api_status_code(response, 200, '/test_view_echo should return HTTP 200.') self.assertEqual(jinx_json.loads(response.content), [[[[[[{'1': now}]]], {'2': delta}]]], "should be able to send and receive datetime.datetime and datetime.timedelta inside complex data structures")
def test_arguments(self): response = self._post_json('/test_view_reverse_three_arguments', [1,2,"3"]) self._assert_api_status_code(response, 200, "/test_view_reverse_three_arguments should return HTTP 200.") self.assertEqual(jinx_json.loads(response.content), ["3", 2, 1], "/test_view_reverse_three_arguments should return the three arguments in reverse order.") response = self._post_json('/test_view_echo', [1]) self._assert_api_status_code(response, 200, '/test_view_echo should return HTTP 200.') self.assertEqual(jinx_json.loads(response.content), [1], "/test_view_echo([1]) should return [1]") response = self._post_json('/test_view_echo', [1,2,3]) self._assert_api_status_code(response, 200, '/test_view_echo should return HTTP 200.') self.assertEqual(jinx_json.loads(response.content), [1,2,3], "/test_view_echo([1]) should return [1,2,3] (variable number of arguments should work)") response = self._post_json('/test_view_reverse_three_arguments', [1, 2]) self._assert_call_status_code(response, 400, '/test_view_reverse_three_arguments with 2 arguments should result in HTTP 400.') response = self._post_json('/test_view_reverse_three_arguments', [1, 2, 3, 4]) self._assert_call_status_code(response, 400, '/test_view_reverse_three_arguments with 4 arguments should result in HTTP 400.') response = self._post_json('/test_view_one_default_argument', ["testarg"]) self._assert_api_status_code(response, 200, '/test_view_one_default_argument with 1 argument should result in HTTP 200.') self.assertEqual(jinx_json.loads(response.content), "testarg") response = self._post_json('/test_view_one_default_argument', []) self._assert_api_status_code(response, 200, '/test_view_one_default_argument with no arguments should result in HTTP 200.') self.assertEqual(jinx_json.loads(response.content), "default", '/test_view_one_default_argument should return "default" if no argument is passed.') response = self._post_json('/test_view_one_default_argument', [1, 2]) self._assert_call_status_code(response, 400, '/test_view_one_default_argument with 2 arguments should result in HTTP 400.')
def _test_serialize(self, data): self.assertEquals(jinx_json.loads(jinx_json.dumps(data)), data)
def process_view(self, request, view, view_args, view_kwargs): """Handle a JSON-based API call. This function is called when Django is about to call a view. It will decode a POST body in JSON format, call the view, and encode the response in JSON format. The request should be a POST with a Content-Type of "application/json". The body of the request should be a JSON-encoded list of arguments to pass to the API call. The arguments will be unpacked and passed in as positional and keyword arguments to the view function as described below. If the view function returns data, it will be JSON-encoded and sent back as a response with Content-Type "application/json". If the view function returns an HttpResponse object, this will be sent back to the client as is. The request body can specify API calls in one of two ways, the old style or the new style. In the old style, only positional arguments may be passed, and the request body is simply a list of positional arguments. In the new style, both positional and keyword arguments may be passed, and the request body is a dict with 'args' and 'kwargs' entries. Old style: ['arg1', 'arg2'] New style: { 'args': ['arg1', 'arg2'], 'kwargs': {'arg3': 'value3, 'arg4': 'value4'} } Arguments: request -- The HttpRequest object from Django. view -- The view function that Django is about to call. view_args -- The position arguments Django would pass to the view. view_kwargs -- The keyword arguments Django would pass to the view. Status Codes Returned: 200 -- The request was completed successfully. 405 -- A method other than POST was used. 415 -- A request body type other than application/json was sent. 500 -- The view function raised an unhandled exception, or returned unserializable data (see exception value for details). ? -- View functions may return whatever HTTP status codes they deem appropriate. See the documentation for each view function. """ content_type = request.META['CONTENT_TYPE'] method = request.META['REQUEST_METHOD'] if method != 'POST': return HttpResponseNotAllowed('The Jinx API requires a POST') if content_type != "application/json": return HttpResponseUnsupportedMediaType('The Jinx API requires a request with Content-Type: application/json') json_data = request.raw_post_data try: json_args = jinx_json.loads(json_data) except ValueError, e: return HttpResponseUnsupportedMediaType('Request body could not be parsed as a JSON object: %s' % str(e))
def __getattr__(self, api_call): """Attribute look up method, which makes an HTTP call to Jinx API Keyword arguments: api_call -- string containing the api call """ def _get_help(api_call_name): """ Generates the url and post body to get the API docstring from the Jinx API. Keyword arguments: api_call_name -- the name of the API call to retrieve documentation for """ if not isinstance(api_call_name, basestring): raise JinxInvalidRequestError("Argument is not a string: '%s'" % api_call_name) if self.interactive: self.auth() url = "https://%s/jinx/%s/%s?doc" %(self.jinx_host, self.api_version, api_call_name) json = jinx_json.dumps(list()) resp_code, resp_hdrs, resp = _post_data(url, json) # If the headers expire, re-auth. if resp_code == 401: self.auth_hdr = None resp_code, resp_hdrs, resp = _post_data(url, json) elif resp_code == 404: raise NameError("API call '%s' does not exist" % api_call_name) _check_resp_code(resp_code, resp_hdrs, resp) print resp return def _call_jinx(*args, **kwargs): """Generates the url and post body to send. Keyword arguments: *args -- python tuple of arguments required for the API call **kwargs -- dict of keyword arguments required for the API call """ url = "https://%s/jinx/%s/%s" %(self.jinx_host, self.api_version, api_call) try: json = jinx_json.dumps({'args': list(args), 'kwargs': kwargs}) except TypeError, e: _be_verbose(e) raise JinxInvalidRequestError("Request arguments are in a non-serializable format: %s" % args) if self.interactive: self.auth() resp_code, resp_hdrs, resp = _post_data(url, json) # If the headers expire, re-auth. if resp_code == 401: self.auth_hdr = None resp_code, resp_hdrs, resp = _post_data(url, json) _check_resp_code(resp_code, resp_hdrs, resp) try: resp = jinx_json.loads(resp) except simplejson.decoder.JSONDecodeError, e: _be_verbose(e) raise JinxInvalidResponseError("Response is not formatted with JSON")