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:

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.

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:

  1. create the car, which will return the car object with an id
  2. 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:

Response:

{
	"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:

Response:

{
	"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:

{
	"name": "John Doe",
	"phone": "44033265",
	"cars": [],
	"comment": "Needs updating."
}

Response:

{
	"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:

{
	"color": "black",
	"registration": "CE423455",
	"comment": "Stolen",
}

Response:


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:

{
	"name": "John Doe",
	"phone": "44033266",
	"cars": [{
		"id": "5323329857"
	}]
}

Response:

{
	"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:

{
	"color": "white" 
}

Response:

{
	"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:

Response:

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:

Commenting your code will help those reviewing the code to understand what you have been thinking.