##Schedule
Week | Activity |
---|---|
1 | Prepare Environment |
2 | Implement Milestone 1 API |
3 | Implement Milestone 2 API |
4 | Implement Encryption, Authentication & Access Control and Unit Tests (Milestone 3) |
5 | Complete Unit Tests for Full Coverage (Milestone 4) |
6 | Test Scaling (Milestone 5) |
7 | Test Performance (Mileston 6) |
8 | Test Reliability (Mielstone 7) |
- Spark
- Jackson
- MongoLink
Recommendations in the following references were used as a general guideline while definig these API specs.
API URL requests have the following components which are supplied by the client
- Method, which determines the type of action being requested on a resource
- Version, which indicates the version of the API being requested
- Resource URI, which identifies the resource that performs the action
- Optional Query String, which specifies additional control parameters for the resource's actions
- Optional Request Body, which specifies data that acts as input to the resource for the action requested
For example, GET http://api.example.com/v1/drivers?count=30
In response, the API returns the following
- HTTP Status Code, indicating the successful or failed outcome of the request
- Response Body, indicating the response of the request made
In addition, there may be Headers in both the request and the response.
Currently supported versions of the API and their release date are provided below. All API calls should be prefixed with
the version number GET /VERSION/cars
even though the version number is not included in the documentation of each
API call.
- Current Version:
v1
(should be used forVERSION
in the example call above)
For example, GET http://api.example.com/v1/drivers?count=30
All resources are named plural (cars
instead of car
) to avoid confusion. No singular references anywhere.
While the API might currently accept some singular versions, that capability will be deprecated. We currently support
the following resources.
Resources that have a relationship to each other can be used as a sub-resource within a URI. For example /drivers/:driverId/cars
identifies the cars for a particular driver while /cars
identifies the global list of cars.
drivers
refers to drivers in the system.
passengers
refers to passengers in the system.
cars
refers to cars in the system. Cars belong to drivers and cannot exist without a driver.
Parameter | Current Applicable Resources | Description |
---|---|---|
count |
all | When returning a list of items, this specifies the maximum elements to return. For example GET /cars?count=100 returns the first hundred cars even if there are more cars in the system |
offsetId |
all | The ID of the last resource already retrieved from a matching list. When provided, server returns resources after this matching element. This is useful in combination with count to get the next page of a list of elements |
sort |
all | Specify the field by which results should be ordered |
sortOrder |
all | Used in combination with sort , it specifies the order in which to return the elements. asc is for asending or desc for descending. Default value is asc except for a time-based sort field in which case the default values is desc |
Note: In this implementation, only support one field for sort
and sortOrder
GET
is used to retrieve either a list of resources or a single resource at a specific URI.- In the case of a list, the data is returned as a JSON array.
- In case of a single resource, it is returned as a JSON object.
POST
is used to create a new resource on the system.- Usually a complete created resource is returned as a JSON object.
- There are rare circumstances where
POST
is used to create multiple resources (such as adding multiple cars to a driver. We will not do it in this project). In this case, a JSON array of created objects or relationships is returned - Note that
POST
is the only method that returns201 Created
on success.
PUT
is used to replace an entire resource at a URI.- A complete copy of the new resource is returned.
- We wil rarely use
PUT
since we mostly update specific properties (seePATCH
below) PUT
currently cannot be applied in bulk to a list of resources
PATCH
is used to update parts of a resource at a URI.- A complete copy of the updated resource is returned.
PATCH
currently cannot be applied in bulk to a list of resources- Our
PATCH
implementation is not compliant with standards but is consistent with generally-used convention. Instead of Request Body being a set of operations, it is a list of fields with updated values
DELETE
is used to remove a resource from the systemDELETE
cannot be applied in bulk to a list of resources
For all successful calls, the return status is 2XX
- For
POST
, the success status code is201 Created
- For all other methods, the success status code is
200 OK
In case of errors, specify the appropriate return status code.
- 400 Bad Request: If there's any problem with the information provided by the client and it cannot be processed properly.
- 401 Unathorized: The credential information provided could not be verified or is invalid
- 404 Not Found: Request is valid, but the resource authorized is not available
- 500 Internal Server Error: Request is OK, but there was unexpected problem on the server
- All calls return a Content-Type of
application/json
In addition to providing one of the error Status Codes mentioned above, the Response Body will contain a JSON object with details of the error.
{
"statusCode": number // Sames as HTTP status code
"errorCode": number // A more detailed error code
"errorMessage": string // human readable error message
}
Following are two types of responses that are returned by the drivers
resources.
{
"id": GUID,
"firstName" : String(50),
"lastName" : String(50),
"emailAddress" : String(50), Valid Format,
"password" : String(20, Min 8)
"addressLine1" : String(100),
"addressLine2" : String(100) - Optional,
"city" : String(50),
"state" : String (2, Min 2),
"zip" : String(5, Min 5)
"phoneNumber" : String - XXX-XXX-XXXX
"drivingLicense" : String(16)
"licensedState" : String(2)
}
[
{
"id": GUID,
"firstName" : String,
"lastName" : String
...
...
}, ...
]
Returns the list of all drivers in the system.
Creates a new driver in the system with the object that is passed to it. All fields in the body below are required. This call returns
an modified JSON containing the internally generated id
and other properties populated by the system.
On failure, this will generate a 400 Bad Request
with a description of the failure.
{
"firstName" : String,
"lastName" : String
...
...
}
Returns a driver matching the provided ID. For example, GET /VERSION/drivers/770877af-7d08-447a-8add-ccfd507ce170
will return a JSON representing the driver with that id.
{
"id": GUID,
"firstName" : String,
"lastName" : String
...
...
}
This will allow partial updates of specific fields in the driver. For example, calling
PATCH /VERSION/drivers/0db87174-4e11-4369-a8fd-8339ed3abd37
with the first Request Body below
will update firstName
property of the driver in the JSON Body. The id
field in the body is optional,
though if present, it will have to match the one in the URL.
This will return a 200 OK
if the update was successful. If the id
or any field in the body conflicts with the request,
a 404 NotFound
will be thrown with the reason.
The response will contain the updated driver.
{
"firstName": "John"
}
or
{
"firstName": "John",
"lastName": "Doe"
...
...
}
This deletes a driver with the given Id from the system
For example, DELETE /VERSION/drivers/0db87174-4e11-4369-a8fd-8339ed3abd37
will delete the driver and return the json back for confirmation with a 200 OK
Following are two types of responses that are returned by the Passengers
resources.
{
"id": GUID,
"firstName" : String(50),
"lastName" : String(50),
"emailAddress" : String(50), Valid Format,
"password" : String(20, Min 8)
"addressLine1" : String(100),
"addressLine2" : String(100) - Optional,
"city" : String(50),
"state" : String (2, Min 2),
"zip" : String(5, Min 5)
"phoneNumber" : String - XXX-XXX-XXXX
}
[
{
"id": GUID,
"firstName" : String,
"lastName" : String
...
...
}, ...
]
Returns the list of all Passengers in the system.
Creates a new passenger in the system with the object that is passed to it. All fields in the body below are required. This call returns
an modified JSON containing the internally generated id
and other properties populated by the system.
On failure, this will generate a 400 Bad Request
with a description of the failure.
{
"firstName" : String,
"lastName" : String
...
...
}
Returns a passenger matching the provided ID. For example, GET /VERSION/passengers/770877af-7d08-447a-8add-ccfd507ce170
will return a JSON representing the passenger with that id.
{
"id": GUID,
"firstName" : String,
"lastName" : String
...
...
}
This will allow partial updates of specific fields in the passenger. For example, calling
PATCH /VERSION/passengers/0db87174-4e11-4369-a8fd-8339ed3abd37
with the first Request Body below
will update firstName
property of the passenger in the JSON Body. The id
field in the body is optional,
though if present, it will have to match the one in the URL.
This will return a 200 OK
if the update was successful. If the id
or any field in the body conflicts with the request,
a 404 NotFound
will be thrown with the reason.
The response will contain the updated passenger.
{
"firstName": "John"
}
or
{
"firstName": "John",
"lastName": "Doe"
...
...
}
This deletes a passenger with the given Id from the system
For example, DELETE /VERSION/passengers/0db87174-4e11-4369-a8fd-8339ed3abd37
will delete the passenger and return the json back for confirmation with a 200 OK
Following are two types of responses that are returned by the cars
resources. For this week's implementation, we will ignore that Cars belong to Drivers and simply implement them as a top-level resource.
{
"id": GUID,
"make": String(50),
"model": String(50),
"license" : String(10),
"carType" : String(10),
"maxPassengers" : number,
"color" : String(20),
"validRideTypes" : String(16) Array // Values are ECONOMY, PREMIUM, EXECUTIVE
}
[
{
"id": GUID,
"make": String(50),
"model": String(50),
...
...
}, ...
]
Returns the list of all Cars in the system.
Creates a new car in the system with the object that is passed to it. All fields in the body below are required. This call returns
an modified JSON containing the internally generated id
and other properties populated by the system.
On failure, this will generate a 400 Bad Request
with a description of the failure.
{
"make": String(50),
"model": String(50),
...
...
}
Returns a car matching the provided ID. For example, GET /VERSION/cars/770877af-7d08-447a-8add-ccfd507ce170
will return a JSON representing the car with that id.
{
"id": GUID,
"make": String(50),
"model": String(50),
...
...
}
This will allow partial updates of specific fields in the car. For example, calling
PATCH /VERSION/cars/0db87174-4e11-4369-a8fd-8339ed3abd37
with the first Request Body below
will update make
property of the car in the JSON Body. The id
field in the body is optional,
though if present, it will have to match the one in the URL.
This will return a 200 OK
if the update was successful. If the id
or any field in the body conflicts with the request,
a 404 NotFound
will be thrown with the reason.
The response will contain the updated car.
{
"make": "Ford"
}
or
{
"make": "Ford",
"model": "Explorer",
...
...
}
This deletes a car with the given Id from the system
For example, DELETE /VERSION/cars/0db87174-4e11-4369-a8fd-8339ed3abd37
will delete the car and return the json back for confirmation with a 200 OK
Following are two types of responses that are returned by the rides
resources. For this week's implementation, we will ignore that rides have Driver, Car, Passenger and Route Points, and simply implement them as a top-level resource.
{
"id": GUID,
"rideType": String(16) // Values are ECONOMY, PREMIUM, EXECUTIVE
"startPoint" : { lat: Decimal, long: Decimal }
"endPoint": { lat: Decimal, long: Decimal }
"requestTime" : Number (Timestamp)
"pickupTime" : Number (TimeStamp)
"dropOffTime" : Number, (TimeStamp)
"status" : String [REQUESTED, AWAITING_DRIVER, DRIVE_ASSIGNED, IN_PROGRESS, ARRIVED, CLOSED]
"fare": Decimal,
}
[
{
"id": GUID,
"rideType": String(16) // Values are ECONOMY, PREMIUM, EXECUTIVE
"startPoint" : { lat: Decimal, long: Decimal }
...
...
}, ...
]
Returns the list of all Rides in the system.
Creates a new ride in the system with the object that is passed to it. All fields in the body below are required. This call returns
an modified JSON containing the internally generated id
and other properties populated by the system.
On failure, this will generate a 400 Bad Request
with a description of the failure.
{
"rideType": "ECONOMY";
"startPoint" : { lat: 34.5, long: 56.7 }
...
...
}
Returns a ride matching the provided ID. For example, GET /VERSION/rides/770877af-7d08-447a-8add-ccfd507ce170
will return a JSON representing the ride with that id.
{
"id": GUID,
"rideType": "ECONOMY";
"startPoint" : { lat: 34.5, long: 56.7 }
...
...
}
This will allow partial updates of specific fields in the car. For example, calling
PATCH /VERSION/rides/0db87174-4e11-4369-a8fd-8339ed3abd37
with the first Request Body below
will update rideType
property of the ride in the JSON Body. The id
field in the body is optional,
though if present, it will have to match the one in the URL.
This will return a 200 OK
if the update was successful. If the id
or any field in the body conflicts with the request,
a 404 NotFound
will be thrown with the reason.
The response will contain the updated ride.
{
"rideType": "ECONOMY";
}
or
{
"rideType": "ECONOMY";
"startPoint" : { lat: 34.5, long: 56.7 }
...
...
}
This deletes a ride with the given Id from the system
For example, DELETE /VERSION/rides/0db87174-4e11-4369-a8fd-8339ed3abd37
will delete the ride and return the json back for confirmation with a 200 OK
The following new API end points will allow cars to be created and managed in the context of a driver.
Other actions like GET
on a single car, PUT
and DELETE
will use the /cars
end point.
Returns the list of all Cars associated with the driver in the system.
Creates a new car in the system associated with the specified driver whose ID is given.
The car is created from the object that is passed to it. All fields in the body below
are required. This call returns
an modified JSON containing the internally generated id
and other properties populated by the system.
On failure, this will generate a 400 Bad Request
with a description of the failure.
{
"make": String(50),
"model": String(50),
...
...
}
Returns a list of rides associated with the passenger.
Returns a list of rides associated with the passenger.
You will delete the following methods (Note: You can repurpose this code for other parts of this milestone)
POST /cars
since you can no longer create cars that are not associated witha driver
Returns the a single element array containing the driver associated with the car
Enhance the ride request object to allow for creation with additional references.
{
"id": GUID,
"rideType": String(16) // Values are ECONOMY, PREMIUM, EXECUTIVE
"startPoint" : { lat: Decimal, long: Decimal }
"endPoint": { lat: Decimal, long: Decimal }
"requestTime" : Number (Timestamp)
"pickupTime" : Number (TimeStamp)
"dropOffTime" : Number, (TimeStamp)
"status" : String [REQUESTED, AWAITING_DRIVER, DRIVE_ASSIGNED, IN_PROGRESS, ARRIVED, CLOSED]
"fare": Decimal,
"driverId" : GUID of the driver,
"carId" : GUID of the car,
"passengerId" : GUID of the passenger
}
Creates a route point on the route taken for the ride. This allows a client to set a series of points on the route that the driver takes for a given ride.
{
timestamp: Number (TImestamp)
lat: Decimal,
long: Decimal
}
Gets the complete list of route points ordered by timestamp (ascending order)
Gets the latest (by timestamp) single route point
In this milestone, you will implement encryption of key data in the system, add authentication and provide access control by implementing two roles for users.
Study and compare encryption and hashing by reading about them on the web. Below are some useful links for this.
- Wikipedia - Encryption
- Secured Password Hashing
- Encryption vs Hashing for Passwords
- Salted Password Hashing
Once done, do the following to complete this port of the Milestone.
- Identify a mechanism for storing password that you think works for you. You can also leverage the technique discussed in the APP class. Create a brief description in your submission document.
- Implement this mechanism for storing the password. Your
POST
method must accept a string password, apply the encryption/hashing mechanism and store the resulting value. - Change your
GET
methods forpassengers
anddrivers
so that the password field is not available in the returned response body.
Modify your code to do the following
- Implement
sessions
API described below. Both drivers and passengers authenticate with this API. You will know who they are by first testing them against drivers and then against passengers.
Implement the following
- Modify
POST
methods fordrivers
andpassengers
so that you cannot have a driver and a passenger with the same email address. - Require authentication before
POST
method is calledrides
andcars
, includingPOST
forcars
within thedrivers
resource. Only drivers can callPOST
method oncars
and, either passengers or drivers can callPOST
methods onrides
. For now, you don't have to restrict that a driver can only create cars for themselves.
Authenticate a user with the following request body
{
"email": "john@doe.com",
"password" : "somepassword"
}
Authenticates the user against the driver and passenger data and returns the following response body
{
"token" : "787897dasd78d7f9s7df98sdf89sd7f89sd7f89sdf"
}
The token should be hashed and encrypted, and contain the user's ID and an expiration date.
In this milestone, you will implement unit tests for all APIs that you created in the previous milestones. Coverage of ALL APIs is required for full credit.
In this milestone, you will do load testing on the APIs that you created in the previous milestones.
- Create a a GCE instance of the smallest size possible (You can start with
n1-standard-1
) - Run load tests on your API. In order to implement this, you will need to create a load test with a common use case (see below)
- Initiate this use case at the rate of 100 instances every 1 minute, and then repeat by halving the interval (30 seonds, 15 seconds etc.)
- Record the response times and plot them.
- Determine the load at which the response time starts to increase non-lineraly
- Create a driver
- Create a car for the driver
- Create a passenger
- Create a ride for the passenger
- Update the ride with the created Driver and a Car
- Add route points every 10 seconds for a 2 minute ride
- Mark the ride as complete
- Repeat by creating 10 rides for the same passenger, car and driver combination.