Assignment 2
Read through the whole assignment before you start working!
Introduction
In this assignment you will build on and extend the Web API from Assignment 1.
Objectives
The objectives for the second assignment are:
- support CRUD operations
- add persistent storage of data
- following key principles of RESTful architecture
Submission
Deadline for this assignment is 23:59 on 1 October 2017.
Submit the assignment to devilry before the deadline, as a .zip-file containing the project files.
In addition, you should share your private github repository with user olavpo
as well as the group teacher in the group you are assigned to (Mustafa: mustafma, Nikolai: njsverdr, August: augusthh).
The Assignment
The assignment is to make a Web API with node.js for managing “Car” (building on assignment 1) and “Owner” objects. The API should support CRUD operations (create, read, update, delete) for both the “Car” and “Owner” endpoints, and persistent storage using MongoDB.
Specifications for Car and Owner
The tables below provide the specification for the Car and Owner objects.
- Required means that the property must be a non-null value.
- Specified in payload denotes whether the property is generated by the server, specified in the payload of the request, or can be specified in the payload but is generated by the server if not included.
Example of requests and the results of the requests are given further below.
Car
Property | Required | Specified in payload | Description |
---|---|---|---|
id | Yes | No | Unique identifier, a randomly generated 10-digit number that is generated when an object is created. |
make | Yes | Yes | The make of the car. |
color | Yes | Yes | The color of the car. |
registration | Yes | Yes | The license plate number, should consist of 2 characters and 6 digits. |
owner | No | Yes | If an owner exists, an object with id and href properties pointing to the owner object. |
comment | No | Yes | Free text comment. |
href | Yes | No | URL of the object, e.g. http://localhost:8081/api/cars/2332225432 |
lastUpdated | Yes | Optional | Timestamp for the last time the object was modified (or created), in ISO 8601 format. |
Owner
Property | Required | Specified in payload | Description |
---|---|---|---|
id | Yes | No | Unique identifier, a randomly generated 10-digit number that is generated when an object is created. |
name | Yes | Yes | Name of owner, string with at least 2 words, each with at least 2 characters. |
phone | Yes | Yes | Phone number, 8 digits. |
cars | Yes | Yes | An array of car objects, each represented by an object with id and href properties pointing to the car objects. |
comment | No | Yes | Free text comment. |
href | Yes | No | URL of the object, e.g. http://localhost:8081/api/owners/8452226432 |
lastUpdated | Yes | Optional | Timestamp for the last time the object was modified (or created), in ISO 8601 format. |
Relationship between Car and Owner
A car can have zero or one owner. An owner can own zero or more cars. The relationship is defined by the owner property on the car objects, and the cars property on the owner objects. To create an owner and a car belonging to that owner, you will need to:
- create the car, which will return the car object with an id
- create the owner, with the cars property including the id of the newly created car
The server should ensure that when requesting either the car or the owner later, the relationship between the two should be reflected in both objects.
Required API endpoints and methods
The Web API should support the following endpoints and HTTP methods:
Endpoint | Methods |
---|---|
/api/cars | GET, POST |
/api/cars/[id] | GET, PUT, PATCH, DELETE |
/api/owners | GET, POST |
/api/owners/[id] | GET, PUT, PATCH, DELETE |
The expected functionality by method is described below.
GET
GET to /api/cars and /api/owners should return a list of all cars and owners, respectively, in JSON representation. Status code should be 200 OK.
GET to /api/cars/[id] and /api/owners/[id] should return one particular car or owner respectively, according to the given id. If no object exists with the given id, the server should return 404 Not found.
Query parameters for filtering objects
On GET requests to /api/cars and /api/owners, there should be support for basic object filtering based on query parameters. The format should be ?filter=[field]:[value]
, for example /api/cars?filter=color:white
should return a list of all cars where the color property is set to white. For properties that return an object, such as the “owner” property on cars, you should use dot notation: /api/cars?filter=owner.id:2321565873
.
POST
POST to /api/cars and /api/owners with a valid JSON payload according to the above specification should return the created object (including the server-generated properties like id and href) with status code 201 Created.
POST to /api/cars/[id] and /api/owners/[id] should return 405 Method not allowed.
PUT
PUT to /api/cars and /api/owners should return 405 Method not allowed.
PUT to /api/cars/[id] and /api/owners/[id] with a valid JSON payload should replace the entire content according to the payload and return the updated object with status code 200 OK. If no object exists with the given id, the server should return 404 Not found.
PATCH
PATCH to /api/cars and /api/owners should return 405 Method not allowed.
PATCH to /api/cars/[id] and /api/owners/[id] with a valid JSON payload should update only the values of properties included in the payload and return the updated object with status code 200 OK. If no object exists with the given id, the server should return 404 Not found.
DELETE
DELETE to /api/cars and /api/owners should return 405 Method not allowed.
DELETE to /api/cars/[id] and /api/owners/[id] should delete the object and return 204 No content. If no object exists with the given id, the server should return 404 Not found.
Invalid payload in POST, PUT and PATCH
POST, PUT and PATH requests with an invalid payload should return 400 Bad request. Examples of this include the the payload is missing required properties (e.g. “name”, “color”), that values are not according to the specification (e.g. “phone” with too few/many digits), and if referring to a non-existing related entity, e.g. if trying to set the “owner” of a car to an id that does not exist.
Unsupported media type
Payloads (POST, PUT, PATCH) in formats other than JSON (Content-Type: application/json) should return 415 Unsupported media type.
GET requests for representations other than JSON (Accept: application/json) should return 406 Not acceptable.
Examples of requests and responses/results
Get all
Get all cars. Similar for owners.
Request:
- Method:
GET
- Endpoint:
http://localhost:8081/api/cars
- Payload: None
Response:
- Status code:
200
- Payload:
{
"cars": [
{
"id": "9123322857",
"make": "Mercedes-Benz",
"color": "white",
"registration": "BN223455",
"owner": {
"id": "2321565873",
"href": "http://localhost:8081/api/owners/2321565873"
},
"comment": null,
"href": "http://localhost:8081/api/cars/1123329857",
"lastUpdated": "2016-08-01T15:00:59.398"
},
{
"id": "6123372857",
"make": "Toyota",
"color": "red",
"registration": "AA343221"
"owner": null,
"comment": null,
"href": "http://localhost:8081/api/cars/6123372857",
"lastUpdated": "2016-08-01T15:00:59.398"
},
{
"id": "5123322227",
"make": "Volvo",
"color": "blue",
"registration": "ZZ532210"
"owner": {
"id": "7421565873",
"href": "http://localhost:8081/api/owners/7421565873"
},
"comment": null,
"href": "http://localhost:8081/api/cars/5123322227",
"lastUpdated": "2016-08-01T15:00:59.398"
}
]
}
Get one
Get single car. Similar for owner.
Request:
- Method:
GET
- Endpoint:
http://localhost:8081/api/cars/1123329857
- Payload: None
Response:
- Status code:
200
- Payload:
{
"id": "1123329857",
"make": "Mercedes-Benz",
"color": "black",
"registration": "BN223455",
"owner": {
"id": "2321565873",
"href": "http://localhost:8081/api/owners/2321565873"
},
"comment": null,
"href": "http://localhost:8081/api/cars/1123329857",
"lastUpdated": "2016-08-01T15:00:59.398"
}
Create
Create a new owner. Similar for cars.
Request:
- Method:
POST
- Endpoint:
http://localhost:8081/api/owners
- Payload:
{
"name": "John Doe",
"phone": "44033265",
"cars": [],
"comment": "Needs updating."
}
Response:
- Status code:
201
- Payload:
{
"id": "1123329857",
"name": "John Doe",
"phone": "44033265",
"cars": [],
"comment": "Needs updating.",
"href": "http://localhost:8081/api/cars/1123329857",
"lastUpdated": "2016-08-01T15:00:59.398"
}
Create - invalid payload
Try to create a new car, with invalid payload. Similar for owner.
Request:
- Method:
POST
- Endpoint:
http://localhost:8081/api/owners
- Payload:
{
"color": "black",
"registration": "CE423455",
"comment": "Stolen",
}
Response:
- Status code:
400
- Payload:
Missing required property "Make"
.
Complete update (replace)
Update an existing owner, changing phone number and specifying ownership of a car. Similar for car.
Existing object (before change):
{
"id": "1123329857",
"name": "John Doe",
"phone": "44033265",
"cars": [],
"comment": "Needs updating.",
"href": "http://localhost:8081/api/owners/1123329857",
"lastUpdated": "2016-08-01T15:00:59.398"
}
Request:
- Method: PUT
- Endpoint:
http://localhost:8081/api/owners/1123329857
- Payload:
{
"name": "John Doe",
"phone": "44033266",
"cars": [{
"id": "5323329857"
}]
}
Response:
- Status code: 200
- Payload:
{
"id": "1123329857",
"name": "John Doe",
"phone": "44033266",
"cars": [{
"id": "5323329857",
"href": "http://localhost:8081/api/cars/5323329857"
}],
"comment": null,
"href": "http://localhost:8081/api/owners/1123329857",
"lastUpdated": "2016-09-15T17:32:50.232"
}
Partial update
Update an existing car, by changing the color. Similar for owner.
Existing object (before change):
{
"id": "1123329857",
"make": "Mercedes-Benz",
"color": "black",
"registration": "BN223455",
"owner": {
"id": "2321565873",
"href": "http://localhost:8081/api/owners/2321565873"
},
"comment": null,
"href": "http://localhost:8081/api/cars/1123329857",
"lastUpdated": "2016-08-01T15:00:59.398"
}
Request:
- Method:
PATCH
- Endpoint:
http://localhost:8081/api/cars/1123329857
- Payload:
{
"color": "white"
}
Response:
- Status code:
200
- Payload:
{
"id": "1123329857",
"make": "Mercedes-Benz",
"color": "white",
"registration": "BN223455",
"owner": {
"id": "2321565873",
"href": "http://localhost:8081/api/owners/2321565873"
},
"comment": null,
"href": "http://localhost:8081/api/cars/1123329857",
"lastUpdated": "2016-08-14T17:02:59.698"
}
Delete
Delete single car. Similar for owner.
Request:
- Method:
DELETE
- Endpoint:
http://localhost:8081/api/owners/2321565873
- Payload: None
Response:
- Status code: 204
- Payload: None
Project structure
For this assignment, you should separate out parts of the application in separate files. There should still be one server.js
file which is used to start the server, but you should also have two separate files car.js
and owner.js
for the data model, i.e. interactions with the database. You can choose whether to keep the routing/controller logic in server.js
or in one or more separate .js
files.
Example of a project structure:
server.js
routes.js #routing logic
models/ #database layer
...car.js #car data model
...owner.js #owner data model
Version control
You should continue using your private git repository from Assignment 1.
Restrictions
For this assignment, you are still not allowed to use frameworks or external libraries when making the API, except the MongoDB driver for node.js.
Finished product
The finished project should have a server.js
file that can be used to start the service, i.e. with node server.js
. It should run on port 8081 (the reason for specifying the port is simply to facilitate testing and grading).
Evaluation
The project will be evaluated to pass/fail based on:
- the Web service running and providing an API that works as specified in the assignment
- code style, i.e. that the code is readable and well organised
- use of git for version control
Commenting your code will help those reviewing the code to understand what you have been thinking.