def test_query_counts(self): # Yes, this is a lot of queries, but this API call is also doing a lot of work :) # - 1 query: Check the user's eligibility and retrieve the credit course # - 1 Get the provider of the credit course. # - 2 queries: Get-or-create the credit request. # - 1 query: Retrieve user account and profile information from the user API. # - 1 query: Look up the user's final grade from the credit requirements table. # - 1 query: Look up the user's enrollment date in the course. # - 2 query: Look up the user's completion date in the course. # - 1 query: Update the request. # - 2 queries: Update the history table for the request. # - 4 Django savepoints with self.assertNumQueries(16): request = api.create_credit_request(self.course_key, self.PROVIDER_ID, self.USER_INFO['username']) # - 2 queries: Retrieve and update the request # - 1 query: Update the history table for the request. uuid = request["parameters"]["request_uuid"] with self.assertNumQueries(3): api.update_credit_request_status(uuid, self.PROVIDER_ID, "approved") with self.assertNumQueries(1): api.get_credit_requests_for_user(self.USER_INFO["username"])
def test_update_invalid_credit_status(self): # The request status must be either "approved" or "rejected" request = api.create_credit_request(self.course_key, self.PROVIDER_ID, self.USER_INFO["username"]) with self.assertRaises(InvalidCreditStatus): api.update_credit_request_status( request["parameters"]["request_uuid"], self.PROVIDER_ID, "invalid")
def test_credit_request_status(self, status): request = api.create_credit_request(self.course_key, self.PROVIDER_ID, self.USER_INFO['username']) # Initial status should be "pending" self._assert_credit_status("pending") # Update the status api.update_credit_request_status(request['uuid'], status) self._assert_credit_status(status)
def test_cannot_make_credit_request_after_response(self, status): # Create the first request request = api.create_credit_request(self.course_key, self.PROVIDER_ID, self.USER_INFO["username"]) # Provider updates the status api.update_credit_request_status(request["parameters"]["request_uuid"], self.PROVIDER_ID, status) # Attempting a second request raises an exception with self.assertRaises(RequestAlreadyCompleted): api.create_credit_request(self.course_key, self.PROVIDER_ID, self.USER_INFO['username'])
def test_credit_request_status(self, status): request = api.create_credit_request(self.course_key, self.PROVIDER_ID, self.USER_INFO["username"]) # Initial status should be "pending" self._assert_credit_status("pending") credit_request_status = api.get_credit_request_status(self.USER_INFO['username'], self.course_key) self.assertEqual(credit_request_status["status"], "pending") # Update the status api.update_credit_request_status(request["parameters"]["request_uuid"], self.PROVIDER_ID, status) self._assert_credit_status(status) credit_request_status = api.get_credit_request_status(self.USER_INFO['username'], self.course_key) self.assertEqual(credit_request_status["status"], status)
def test_query_counts(self): # Yes, this is a lot of queries, but this API call is also doing a lot of work :) # - 1 query: Check the user's eligibility and retrieve the credit course and provider. # - 2 queries: Get-or-create the credit request. # - 1 query: Retrieve user account and profile information from the user API. # - 1 query: Look up the user's final grade from the credit requirements table. # - 2 queries: Update the request. # - 2 queries: Update the history table for the request. with self.assertNumQueries(9): request = api.create_credit_request(self.course_key, self.PROVIDER_ID, self.USER_INFO['username']) # - 3 queries: Retrieve and update the request # - 1 query: Update the history table for the request. with self.assertNumQueries(4): api.update_credit_request_status(request['uuid'], "approved") with self.assertNumQueries(1): api.get_credit_requests_for_user(self.USER_INFO['username'])
def test_query_counts(self): # Yes, this is a lot of queries, but this API call is also doing a lot of work :) # - 1 query: Check the user's eligibility and retrieve the credit course # - 1 Get the provider of the credit course. # - 2 queries: Get-or-create the credit request. # - 1 query: Retrieve user account and profile information from the user API. # - 1 query: Look up the user's final grade from the credit requirements table. # - 1 query: Update the request. # - 2 queries: Update the history table for the request. # - 4 Django savepoints with self.assertNumQueries(13): request = api.create_credit_request(self.course_key, self.PROVIDER_ID, self.USER_INFO["username"]) # - 2 queries: Retrieve and update the request # - 1 query: Update the history table for the request. uuid = request["parameters"]["request_uuid"] with self.assertNumQueries(3): api.update_credit_request_status(uuid, self.PROVIDER_ID, "approved") with self.assertNumQueries(1): api.get_credit_requests_for_user(self.USER_INFO["username"])
def test_update_credit_request_not_found(self): # The request UUID must exist with self.assertRaises(CreditRequestNotFound): api.update_credit_request_status("invalid_uuid", self.PROVIDER_ID, "approved")
def credit_provider_callback(request, provider_id): """ Callback end-point used by credit providers to approve or reject a request for credit. **Example Usage:** POST /api/credit/v1/providers/{provider-id}/callback { "request_uuid": "557168d0f7664fe59097106c67c3f847", "status": "approved", "timestamp": 1434631630, "signature": "cRCNjkE4IzY+erIjRwOQCpRILgOvXx4q2qvx141BCqI=" } Response: 200 OK **Parameters:** * request_uuid (string): The UUID of the request. * status (string): Either "approved" or "rejected". * timestamp (int or string): The datetime at which the POST request was made, represented as the number of seconds since January 1, 1970 00:00:00 UTC. If the timestamp is a string, it will be converted to an integer. * signature (string): A digital signature of the request parameters, created using a secret key shared with the credit provider. **Responses:** * 200 OK: The user's status was updated successfully. * 400 Bad request: The provided parameters were not valid. Response content will be a JSON-encoded string describing the error. * 403 Forbidden: Signature was invalid or timestamp was too far in the past. * 404 Not Found: Could not find a request with the specified UUID associated with this provider. """ response, parameters = _validate_json_parameters( request.body, ["request_uuid", "status", "timestamp", "signature"]) if response is not None: return response # Validate the digital signature of the request. # This ensures that the message came from the credit provider # and hasn't been tampered with. response = _validate_signature(parameters, provider_id) if response is not None: return response # Validate the timestamp to ensure that the request is timely. response = _validate_timestamp(parameters["timestamp"], provider_id) if response is not None: return response # Update the credit request status try: api.update_credit_request_status(parameters["request_uuid"], provider_id, parameters["status"]) except CreditRequestNotFound: raise Http404 except CreditApiBadRequest as ex: return HttpResponseBadRequest(ex) else: return HttpResponse()
def _set_request_status(self, uuid, status): """Set the status of a request for credit, simulating the notification from the provider. """ credit_api.update_credit_request_status(uuid, self.PROVIDER_ID, status)
def test_update_invalid_credit_status(self): # The request status must be either "approved" or "rejected" request = api.create_credit_request(self.course_key, self.PROVIDER_ID, self.USER_INFO["username"]) with self.assertRaises(InvalidCreditStatus): api.update_credit_request_status(request["parameters"]["request_uuid"], self.PROVIDER_ID, "invalid")
def credit_provider_callback(request, provider_id): """ Callback end-point used by credit providers to approve or reject a request for credit. **Example Usage:** POST /api/credit/v1/providers/{provider-id}/callback { "request_uuid": "557168d0f7664fe59097106c67c3f847", "status": "approved", "timestamp": 1434631630, "signature": "cRCNjkE4IzY+erIjRwOQCpRILgOvXx4q2qvx141BCqI=" } Response: 200 OK **Parameters:** * request_uuid (string): The UUID of the request. * status (string): Either "approved" or "rejected". * timestamp (int or string): The datetime at which the POST request was made, represented as the number of seconds since January 1, 1970 00:00:00 UTC. If the timestamp is a string, it will be converted to an integer. * signature (string): A digital signature of the request parameters, created using a secret key shared with the credit provider. **Responses:** * 200 OK: The user's status was updated successfully. * 400 Bad request: The provided parameters were not valid. Response content will be a JSON-encoded string describing the error. * 403 Forbidden: Signature was invalid or timestamp was too far in the past. * 404 Not Found: Could not find a request with the specified UUID associated with this provider. """ response, parameters = _validate_json_parameters(request.body, [ "request_uuid", "status", "timestamp", "signature" ]) if response is not None: return response # Validate the digital signature of the request. # This ensures that the message came from the credit provider # and hasn't been tampered with. response = _validate_signature(parameters, provider_id) if response is not None: return response # Validate the timestamp to ensure that the request is timely. response = _validate_timestamp(parameters["timestamp"], provider_id) if response is not None: return response # Update the credit request status try: api.update_credit_request_status(parameters["request_uuid"], provider_id, parameters["status"]) except CreditRequestNotFound: raise Http404 except CreditApiBadRequest as ex: return HttpResponseBadRequest(ex) else: return HttpResponse()