class UsersController: """This is a controller for the main /users requests""" # The JSON schema expected for requests docRequest_login = """ <ul> <li> The request must be a POST to /user/login with Content-Type "application/json" <li> The data in the request will be a JSON dictionary of the form given below <li> The two fields in the request are ASCII strings </ul> """ schemaRequest_loginOrAdd = Schema({ 'user': '******', 'password': '******' }) # The JSON schema ensured for responses docResponse_login = """ <ul> <li> The request must be a POST to /user/login or /user/add with Content-Type "application/json" <li> The response will be a JSON dictionary of the form given below <li> The errCode field is an integer with the same values as for the login/add method of UsersModel class <li> The count is present only if errCode is SUCCESS, and is the count of logins for the current user <li> The response will use HTTP status code 200 unless there is a catastrophic error outside the ones captured by the error codes (e.g., an unhandled exception). In that case a status code of 500 should be used. </ul> """ schemaResp_loginOrAdd = Schema({ 'errCode': 1 }).when(errCode__eq=UsersModel.SUCCESS, doc='Additional fields on SUCCESS', update={'count': 15}) def do_POST(self, request): if request.path == "/users/login" or request.path == "/users/add": # Most of the code for "login" and "add" is the same rdata = request.getRequestData( requestName=request.path, requestSchema=UsersController.schemaRequest_loginOrAdd) if not rdata: return username = rdata["user"] password = rdata["password"] if request.path == "/users/login": rval = g_users.login(username, password) else: rval = g_users.add(username, password) if rval < 0: resp = {"errCode": rval} else: resp = {"errCode": UsersModel.SUCCESS, "count": rval} request.sendResponse(data=Utils.jsonDumps( resp, objName='resp:' + request.path, schema=UsersController.schemaResp_loginOrAdd)) else: return request.send_error(404, "Unrecognized request")
def jsonDumps(data, objName="", schema=None): """ Serialize data with JSON, optionally checking the schema @param data: @param objName: @param schema: @return: """ if schema == None: msg = "No schema given for JSON serialization of "+objName Utils.log(msg, err=True) raise Exception(msg) # Make sure we fail the request else: Schema.validate(schema, data, objName=objName) return json.dumps(data)
def jsonDumps(data, objName="", schema=None): """ Serialize data with JSON, optionally checking the schema @param data: @param objName: @param schema: @return: """ if schema == None: msg = "No schema given for JSON serialization of " + objName Utils.log(msg, err=True) raise Exception(msg) # Make sure we fail the request else: Schema.validate(schema, data, objName=objName) return json.dumps(data)
def jsonLoads(dataStr, objName="", schema=None): """ Deserialize data with JSON, optionally checking the schema @param dataStr: @param objName: @param schema: @return: """ data = json.loads(dataStr) if schema == None: msg = 'No schema given for JSON deserialization of '+objName Utils.log(msg, err=True) raise Exception(msg) # Make sure we fail the request else: Schema.validate(schema, data, objName=objName) return data
def jsonLoads(dataStr, objName="", schema=None): """ Deserialize data with JSON, optionally checking the schema @param dataStr: @param objName: @param schema: @return: """ data = json.loads(dataStr) if schema == None: msg = 'No schema given for JSON deserialization of ' + objName Utils.log(msg, err=True) raise Exception(msg) # Make sure we fail the request else: Schema.validate(schema, data, objName=objName) return data
def generateSchema(outf, header="h2", anchor="", title="", descr="", schema=None): generateSection(outf, header=header, anchor=anchor, title=title) outf.write(descr + "\n") html = Schema.htmlDoc(schema) outf.write(html + "\n")
def generateSchema(outf, header="h2", anchor="", title="", descr="", schema=None): generateSection(outf, header=header, anchor=anchor, title=title) outf.write(descr+"\n") html = Schema.htmlDoc(schema) outf.write(html+"\n")
outf.write(TESTAPI_Controller.docReq_resetFixture) outf.write(TESTAPI_Controller.docResp_resetFixture) generateSchema(outf, schema=TESTAPI_Controller.schemaResp_resetFixture) generateSection(outf, header="h2", title='/TESTAPI/unitTests request', anchor='unitTests') outf.write(TESTAPI_Controller.docReq_unitTests) outf.write(TESTAPI_Controller.docResp_unitTests) generateSchema(outf, schema=TESTAPI_Controller.schemaResp_unitTests) Schema.registerErrorReporter(Utils.schemaErrorReporter) ### ### Main entry point ### def run(): """ Main entry point """ port = int(os.environ.get("PORT", 5000)) # We use port 5000 to please Heroku sys.stderr.write('http server is starting on 0.0.0.0:'+str(port)+'...\n') #ip and port of servr #by default http server port is 80 server_address = ('0.0.0.0', port)
class TESTAPI_Controller: """This is a controller for the special TESTAPI_ interface to the server.""" docReq_resetFixture = """ <ul> <li> The request must be a POST to /TESTAPI/resetFixture with Content-Type "application/json" <li> The data is an empty dictionary </ul> """ docResp_resetFixture = """ <ul> <li> Upon receiving this request the back-end should reset the databases to their empty state. For this project, this will consist of calling the UsersModel TESTAPI_resetFixture method. <li> The response should be a JSON dictionary with the contents described below <li> Note: <i>Real life projects do not contains such a public API. Instead the tests would be run on a special test database. We added this API so that we can test your backend easily even if do not have direct access to the database.</i> </ul> """ schemaResp_resetFixture = Schema({ 'errCode': Schema(1, doc="The error code", valid=SchemaValidator.eq(1)) }) docReq_unitTests = """ <ul> <li> The request must be a POST to /TESTAPI/unitTests with Content-Type "application/json" <li> The data is an empty dictionary </ul> """ docResp_unitTests = """ <ul> <li> Upon receiving this request the backend should run all of the unit tests, wait for them to complete, extract the number of tests, successes, failures, and the complete output of the tests and package that as part of the response <li> The response should contain a JSON dictionary with the fields described below <li> If there is a major error running the unit tests, then the response should at least contain the 'output' field with some error message. <p> One possible strategy for implementing this is to run the unit tests as separate shell command, redirecting the output to a file. Once the tests complete, you read the output and extract the necessary information. <li> Note: <i>Real life projects do not contains such a public API. Instead the tests would be run on a special test database. We added this API so that we can test your backend easily even if do not have direct access to the database.</i> </ul> """ schemaResp_unitTests = Schema({ 'totalTests': Schema(5, doc='how many unit tests were executed'), 'nrFailed': Schema(3, doc='how many unit tests failed'), 'output': Schema(" ... ", doc='The output of running the tests') }) def do_POST(self, request): # Note: This is added functionality to make unit testing easier if request.path == "/TESTAPI/resetFixture": g_users.TESTAPI_resetFixture() # To simplify the testing, make this be a JSON object request.sendResponse(data=Utils.jsonDumps( {"errCode": UsersModel.SUCCESS}, objName="resetFixture_resp", schema=TESTAPI_Controller.schemaResp_resetFixture)) return elif request.path == "/TESTAPI/unitTests": # We run the unit tests and collect the output into a temporary file # Conveniently, we have a Makefile target for all unit_tests # There are better ways of doing this in Python, but this is a more portable example (ofile, ofileName) = tempfile.mkstemp(prefix="userCounter") try: errMsg = "" # We accumulate here error messages output = "" # Some default values totalTests = 0 nrFailed = 0 while True: # Give us a way to break # Find the path to the server installation thisDir = os.path.dirname(os.path.abspath(__file__)) cmd = "make -C " + thisDir + " unit_tests >" + ofileName + " 2>&1" Utils.log("Executing " + cmd) code = os.system(cmd) if code != 0: # There was some error running the tests. # This happens even if we just have some failing tests errMsg = "Error running command (code=" + str( code) + "): " + cmd + "\n" # Continue to get the output, and to parse it # Now get the output try: ofileFile = open(ofileName, "r") output = ofileFile.read() ofileFile.close() except: errMsg += "Error reading the output " + traceback.format_exc( ) # No point in continuing break Utils.log("Got " + output) # Python unittest prints a line like the following line at the end # Ran 4 tests in 0.001s m = re.search(r'Ran (\d+) tests', output) if not m: errMsg += "Cannot extract the number of tests\n" break totalTests = int(m.group(1)) # If there are failures, we will see a line like the following # FAILED (failures=1) m = re.search('rFAILED.*\(failures=(\d+)\)', output) if m: nrFailures = int(m.group(1)) break # Exit while # End while if errMsg: Utils.log(errMsg, err=True) resp = { 'output': errMsg + output, 'totalTests': totalTests, 'nrFailed': nrFailed } request.sendResponse(data=Utils.jsonDumps( resp, objName="unitTests_resp", schema=TESTAPI_Controller.schemaResp_unitTests)) finally: os.unlink(ofileName) else: return request.send_error(404, "Unrecognized request")
outf.write(TESTAPI_Controller.docReq_resetFixture) outf.write(TESTAPI_Controller.docResp_resetFixture) generateSchema(outf, schema=TESTAPI_Controller.schemaResp_resetFixture) generateSection(outf, header="h2", title='/TESTAPI/unitTests request', anchor='unitTests') outf.write(TESTAPI_Controller.docReq_unitTests) outf.write(TESTAPI_Controller.docResp_unitTests) generateSchema(outf, schema=TESTAPI_Controller.schemaResp_unitTests) Schema.registerErrorReporter(Utils.schemaErrorReporter) ### ### Main entry point ### def run(): """ Main entry point """ port = int(os.environ.get("PORT", 5000)) # We use port 5000 to please Heroku sys.stderr.write('http server is starting on 0.0.0.0:' + str(port) + '...\n') #ip and port of servr