EECS3311 - Software Design Course Project
York University Fall 2023
Microservices
DESCRIPTION
The nature of software development projects in the industry is one of general requirements and deadlines. You’ll come across a lot of unknowns in this assignment; use your best judgment to resolve them. This is not to hurt you, but it will prepare you for the industry (which many of you will be going into during your internship years). Please note that we may choose not answer all your questions, as that would be a failure on our part to adequately teach you the skills to become effective Software Engineers.
This project is worth 15% of your final mark, divided in phases as follows: -Phase I due Nov 30, worth 5% of your final mark
-Phase II due Dec 5, worth 10% of your final mark
You will be creating a backend Java API using 2 microservices, that can be communicated with to accomplish specific use-cases. You will be using MongoDB and Neo4J databases for the two services, and will be implementing this assignment using the Java Spring framework.
OBJECTIVE
1. Explore NoSQL (MongoDB) / Graph Database (Neo4j)
2. To practice the implementation of background business logic to support REST API
endpoints that provide access to read and modify data in a variety of databases
3. Practice Software Architecture, in particular Server/Client model
4. Learn how to extend functionality of a software project by adding new features.
5. Practice using a build automation tool such as Maven.
Phase I - PROJECT/IDE SETUP AND NEW FEATURES
This assignment requires you to have the following software:
● Java 1.8 (resources will be provided on how to switch your Java version)
● Latest version of Neo4j Desktop
● Latest version of Maven
● Latest version of MongoDB
If you submit an assignment using the wrong Java version or if you change any of the versions specified in the pom.xml, your code may not work on our machines, and any remark requests will be rejected.
How to run your Java applications using the Command Line:
● Install Maven
● To compile your code simply run mvn compile in the root directory of the specific
microservice application (the folder that has the pom.xml for that application)
● To run the compiled microservice application, run mvn spring-boot:run
There will be separate instructions provided, that will comprehensively explain how to install and run your Neo4j and MongoDB database servers.
HOW TO SUBMIT YOUR WORK AND WHAT TO SUBMIT FOR PHASE I
1. Your work will be submitted through eClass.
2. If you are working with a partner, please fill in the team contract (a sample is posted on
eClass). If you are working alone please skip this step.
3. Please study this handout in full (including parts listed under Phase II of this handout) and complete the setup of your project.
4. Create a PDF (for submission) with the following content:
○ Introduce yourself (and your team mates), describe shortly what do you intend to learn in this project, by identifying a personal learning goal (i.e. you may decide to use a version tracking software to track your work, such as git, etc.)
○ Give a short description (at most two paragraphs) in your own words, to the application that you will develop. You may rephrase material from the handout, but you need to explain it in your own words.
○ Identify one to two new features (one if working solo, two if in a group). A sample feature might be:
i. Add a new property to songs (i.e. genre) and add an endpoint that generates a playlist for a given genre.
ii. The above sample feature may not be used as your feature, otherwise you will get 0 for this portion of your grade in both Phase 1 and 2.
○ Specify the type of the endpoint(s) (POST/GET/etc) that you will need to implement for your new feature(s), and create a description of those endpoint(s) following the examples in this handout.
○ Describe how you will test each feature for your video.
○ Complete your work by reporting about your setup, including screenshots of your
project setup in your chosen IDE.
○ Submit this PDF by the deadline on eClass as a group. A group makes one submission.
PHASE II - HOW TO SUBMIT YOUR WORK AND WHAT TO SUBMIT
Your work will be submitted through eClass.
Please ensure that your code runs on the Linux lab machines correctly prior to submitting it
Please submit the two Eclipse projects under profile-microservice and song-microservice. Additionally, record a video of your project showing a quick demo for the required features, plus the new features added by your team.
The project directory must look like the following (you can ignore the target folders in the song and profile microservices):
STARTER FILES
• projectf23/profile-microservice - Contains the project that is responsible for the Profile microservice
• projectf23/song-microservice - Contains the project that is responsible for the Song microservice
• *Controller Classes - Microservice routes are placed here. This is the class that will be receiving and returning response to Clients
• In the controller classes, you will find that there are attributes annotated with something called “Autowired”. This annotation is used by Spring Boot to ensure that it will only instantiate one instance of that type of object. The corresponding class will have an annotation like @Component, @Service, @Repository, etc. to register it as a bean with Spring Boot.
• *Impl Classes - Microservice database operations and functions are placed here
• DbQueryStatus.java - The object that needs to be returned from every database operation which includes the result and possible data that the route may require
• DbQueryExecResult.java - The status of DB function calls
• The following files need to be implemented:
◦ ProfileDriverImpl.java
◦ PlaylistDriverImpl.java
◦ ProfileController.java
◦ SongController.java
◦ SongDalImpl.java
REQUIREMENTS
REST API endpoints
Each REST API must return the HTTP 200 OK status if the operation was successful; otherwise the appropriate not-”OK” status must be returned (such as 500 “INTERNAL_SERVER_ERROR”, 404 “NOT_FOUND”, 400 “BAD_REQUEST”, etc.). Please use your best judgement for what kind of error code you should return for those non-happy paths.
Additionally, there is also a status property in the response body being returned that you’ll need to populate.
An example of how you can inject the status to the response body, as well as set the status in the response header, has been provided in SongController.getSongById. It utilizes Utils.setResponseStatus to check the DB query status, and then sets the return status values accordingly. You can copy this paradigm for the other controller methods.
Furthermore, all of the microservice routes will also return the path that was called via the “path” property (you don’t have to modify this part of the code).
This is mostly for your own reference, we will not be checking this when marking.
Song Microservice
Contains all Song information and routes. This service must run on port 3001
• GET /getSongTitleById/{songId}
◦ Description: Retrieves the song title given the song
◦ URL Parameters: songId - a String that represents the _id of a specific song
◦ Expected Response:
▪ “status”
▪ “OK”, if the title was retrieved successfully
▪ <string>, any appropriate error status if the song was not retrieved
successfully
▪ “data”: <string>, the title of the song that was retrieved
◦ Example Response:
{
"status": "OK",
"data": "Never going to give you up" }
• DELETE /deleteSongById/{songId}
◦ Description: Deletes the song from the MongoDB database and from all Profiles
that have the songId added in their “favourites” list
◦ URL Parameters: songId - a String that represents the _id of a specific song
◦ Expected Response:
▪ “status”
▪ “OK”, if the song was deleted successfully from all locations
▪ <string>, any other status if the song was unable to be deleted
• POST /addSong
◦ Description: Adds a Song to the Song database
◦ Body Parameters:
▪ songName - The name of the song
▪ songArtistFullName - The name of the song artist
▪ songAlbum - The album the song is a part of
◦ Expected Response:
▪ “status”
▪ “OK”, if the song was deleted successfully from all locations
▪ <string>, any other status if the song unable to be added
▪ “data” - The songName, songArtistFullName, songAlbum,
songAmountFavourites, and the id of the Song should all be returned if the
Song is added successfully
◦ Example Response:
{
"data": {
"songName": "testDeleteSong", "songArtistFullName": "111", "songAlbum": "testDeleteSongArtist", "songAmountFavourites": "0",
"id": "5d65df689a2efe318808f4cc" }
"status": "OK" }
• PUT /updateSongFavouritesCount/{songId}?shouldDecrement={trueorfalse}
◦ Description: Updates the song’s favourites count
◦ URL Parameters:
▪ songId - a String that represents the _id of a specific song ◦ Query Parameters:
▪ shouldDecrement - States whether the song count should be incremented or decremented by 1. Accepts string values of “true” or “false”
◦ Expected Response: ▪ “status”
▪ “OK”, if the song was updated successfully from all locations
▪ <string>, any other status if the song’s favourite count was unable
to be updated.
Profile Microservice
Contains all Profile and Playlist information and routes. This service must run on port 3002
• POST /profile
◦ Description: Adds a profile to the Profile database, creates userName-favorites
playlist and creates relation (nProfile:profile)-[:created]->(nPlaylist:playlist)
◦ Body Parameters:
▪ userName - The name of the Profile
▪ fullName - The full name of the User of the profile
▪ password- The password associated with the Profile
◦ Expected Response:
▪ “status”
▪ “OK”, if Profile is created and added correctly to the database
▪ <string>, any other status if the Profile was unable to be created
• PUT /followFriend/{userName}/{friendUserName}
◦ Description: Allows a Profile to follow another Profile and become a friend
◦ URL Parameters:
▪ userName - The userName of the Profile that will follow another Profile
▪ friendUserName - The userName of the Profile that will be followed
◦ Expected Response:
▪ “status”
▪ “OK”, if the user is able to follow the specified User
▪ <string>, any other status if the Profile was unable to follow the
specified Profile
• PUT /unfollowFriend/{userName}/{friendUserName}
◦ Description: Allows a Profile to unfollow another Profile and no longer be “friends”
with them
◦ URL Parameters:
▪ userName - The userName of the Profile that will unfollow another Profile
▪ friendUserName - The userName of the Profile that is being unfollowed
◦ Expected Response:
▪ “status”
▪ “OK”, if the user is able to unfollow the specified User
▪ <string>, any other status if the Profile was not able to unfollow the
specified Profile
• PUT /likeSong/{userName}/{songId}
◦ Description: Allows a Profile to like a song and add it to their favourites. A Profile
cannot like a song more than once.
◦ URL Parameters:
▪ userName - The userName of the Profile that is going to like the song (i.e. add the song to their favourites)
▪ songId - a String that represents the _id of the song that needs to be liked
◦ Expected Response:
▪ “status”
▪ “OK”, if the User is able to like the Song
▪ <string>, any other status if the Profile was not able to like the
specified Song
• PUT /unlikeSong/{userName}/{songId}
◦ Description: Allows a Profile to unlike a song and remove it from their favourites. A
profile cannot unlike a song that it doesn’t already like.
▪ URL Parameters:
▪ userName - The userName of the Profile that is going to unlike the song
▪ songId - a String that represents the _id of the song that needs to be unliked
▪ Expected Response:
▪ “status”
▪ “OK”, if the User is able to unlike the Song
▪ <string>, any other status if the Profile was not able to
unlike the specified Song (including going below 0)
• GET /getAllFriendFavouriteSongTitles/{userName}
◦ Description: Returns the Song names of all of the Songs that the User’s friends
have liked
▪ URL Parameters:
▪ userName - The userName of the Profile whose friends songs we will retrieve
▪ songId - a String that represents the _id of the song that needs to be unliked
▪ Expected Response:
▪ “status”
▪ “OK”, if the User is able to unlike the Song
▪ <string>, any other status if the Profile was not able to
unlike the specified Song
▪ Example Response:
{
"data": {
"tahmid": [
"The Lego Movie", "Henry IV",
"Land of Milk and Honey"
], "shabaz": [
"Sliver",
"Off the Black", "The Lego Movie"
] },
"status": "OK" }
Database
You also have to implement the following DB methods in each respective service to use within the routes. Below is a description of what each DB method should do.
Song Microservice
• The following requirements are implemented in the starter code for you, but please ensure that the following requirements are satisfied
◦ You database must be called “eecs3311-test”
◦ Your collection must be called “songs”
◦ Your song database must be connected to port 27017
• Song Schema
◦ _id - The ObjectId of the document (i.e. a unique identifier for each document).
The id is auto-generated by Mongo when a document is inserted into the
database
◦ songName - The name of the song
◦ songArtistFullName - The full name of the artist of the song
◦ songAlbum - The album the song belongs to
◦ songAmountFavourites - The amount of Profiles that have favourited the Song
• DB Methods to Implement
◦ DbQueryStatus addSong(Song songToAdd) - Adds the specified song to the
MongoDB
◦ DbQueryStatus findSongById(String songId) - Returns the specified Song that
matches the songId
◦ DbQueryStatus getSongTitleById(String songId) - Returns the song title which
matches the songId
◦ DbQueryStatus deleteSongById(String songId) - Deletes the specified song from
the MongoDB
◦ DbQueryStatus updateSongFavouritesCount(String songId, boolean
shouldDecrement) - Increments/decrements a Song's Like Count. You should not be able to decrement a song’s likes below 0.
Profile Microservice
• The following requirements are implemented in the starter code for you, but please ensure that the following requirements are satisfied
◦ Your profile database username and password must be “neo4j" and "12345678” respectively
◦ Your profile database must be connected to 7687
• Neo4j database schema MUST follow the schema below:
◦ The nodes MUST be exactly:
▪ “profile”
▪ “song”
▪ “playlist”
◦ The relationships MUST be exactly
▪ (:profile)-[:created]->(:playlist)
▪ (profile)-[:follows]->(:profile)
▪ (:playlist)-[:includes]->(:song)
◦ Each “song” node must have a “songId” attribute
◦ The plName should be {userName}-favorites
• DB Methods to Implement
◦ DbQueryStatus likeSong(String userName, String songId) - Adds the songId to the Profile’s favourites list
◦ DbQueryStatus unlikeSong(String userName, String songId) - Removes the songId from the Profile’s favourites list
◦ DbQueryStatus deleteSongFromDb(String songId) - Deletes the songId from all favourites playlist
◦ DbQueryStatus createUserProfile(String userName, String fullName, String password) - Creates the Profile and adds it to the Neo4J DB
◦ DbQueryStatus followFriend(String userName, String friendUserName) - Allows the userName Profile to follow the friendUserNameProfile
◦ DbQueryStatus unfollowFriend(String userName, String friendUserName) - Allows the userName Profile to unfollow the friendUserNameProfile
◦ DbQueryStatus getAllSongFriendsLike(String userName) - Returns a list of all the songIds for all of userName Profile’s friends
Expected behaviour for additional ungraded routes
Some routes in the ‘Database’ section of the handout are not mentioned in the Requirement section (/findSongById, , ). Also, there are routes not mentioned in the starter code ( ) which also fall into this category. For these routes, you are free to choose the behaviour and return format as we will not be calling these routes directly for testing. These are for you to call in your microservice while implementing and testing them yourselves. Make sure it’s convenient for you to use!
/deleteSongFromDb
/getAllSongFriendsLike
/deleteAllSongsFromDb
Design Choice
Note that the design choices for the interactions and internal structure of both the profile microservice and song microservice is up to you. However, try to think about which way is more efficient and convenient for you. After all, you are in a software design course.
The one major expectation is that the profile microservice will have to contact the song microservice in order to collect information about songs (specifically for the getAllFriendFavouriteSongTitles endpoint). To do this, you can use okhttp3 to create an internal HTTP client in your profile microservice Java code to interact with the REST API endpoints of the song microservice.
Resources about okhttp3 will be provided.
For testing, when we call a route, we want to see that you perform the expected action, you return the expected result, and the database(s) is/are modified to reflect this. Try to design your API so that it delivers the requirements and handles the points you brought up to the best of its ability.
Git Usage
It is highly recommended to use Git for version control of your project, and you should back up your work to a repository such as GitHub regularly. If you are working in a group, you should be using GitHub to properly work collaboratively.
Code Style
You will be required to appropriately use variable naming conventions, file naming conventions in both microservices. You will also be required to comment your routes and function signatures appropriately.
Allowed Imports
We permit all of the following to be imported and used in this project:
- Whatever is already present in the starter code
- java.util.*
- java.io.*
- java.lang.*
- org.json.*
- org.bson.* (needed for MongoDB)
- org.springframework.* (specifically, you may be interested in stuff under
org.springframework.data.mongodb package for using the Spring Framework to communicate
with your MongoDB database server)
- org.neo4j.driver.v1.*
- okhttp3.* (you will use this to allow the profile microservice to communicate with the song
microservice)
If you would like to write JUnit tests at all, the starter test classes are all using JUnit 4. Therefore, imports related to JUnit 4 are permitted.
TESTING YOUR CODE
Run your services. See Environment Setup for how to run each service
CLI
From the command line, the best way to test your endpoints is using curl.
• The -X flag specifies the REST action you wish to use
• The --data flag specifies the body of the request if one is required
Postman
Postman is a very nice tool for test HTTP endpoints. With its user interface, you are allowed to specify many options, such as query parameters, request bodies, authorization tokens, headers, etc.
An additional guide on Postman by Devin is linked here.
This guide contains very important information on sending a request and reading a response, and also has additional information, such as how to create a request collection.
curl -X POST