The developed system is a software backend which is aimed at handling games of "Italian Dame" between different users. After being authenticated through a JSON Web Token (JWT), the users can choose between different actions (routes) that will allow them to create games, make moves, quit games etc, ect...
All users' profiles, as soon as created, are associated with a token credit; this tokens are consumed after starting a game (-0.35 tokens) and moving pawns (-0.015 for each move); once a user has no tokens left, it will be impossible for him/her to interact with the system.
The software provides a default administrator account, which allows to give users new tokens. It is also possible for all players to get information about any game played in the past, in addition to statistics about other users.
Role | Action | Description |
---|---|---|
User/Admin | Create Game | Create a new game indicating the grid dimension (which has to be greater than 4) and the opponent's email. Upon creation 0.35 tokens are withdrawn from user's account. |
User/Admin | Move Pawn | Move a specific pawn in the game you are playing, indicating the different cells that the pawn has to go through (cells could be more than 1 in case the pawn eliminates different enemy pawns). |
User/Admin | Game State | Obtain information (creator and opponent's emails, state, turn, winner and pawns' positions) about the state of any game (terminated or not). |
User/Admin | Quit Game | The user leaves the game he/her is playing in at the moment. This will attribute a win to the opponent and a loss to the user. |
User/Admin | Game Moves | Obtain a list (JSON or CSV) off all moves done by all pawns in a specific game (terminated or not). |
User/Admin | Get Ranking | Obtain a list (ascending or descending) of all players sorted by their number of wins. |
User/Admin | Get Stats | Get statistics (total games, nΒ° of wins/losses, games won due to abandon/games lost due to abandon, avarage moves to win/lose) about a specific player. |
User/Admin | Token Balance | Allows the user to check how many tokens he/her has left (This action is the only one which doesn't require the user to have a token balance greater than 0). |
Admin | Set Tokens | The Admin user can set any users' token balance to a certain level. |
Action | Method | Route |
---|---|---|
Create Game | POST | /game |
Move Pawn | POST | /move |
Game State | GET | /gameInfo/:id |
Quit Game | POST | /:id/quit |
Game Moves | GET | /gameMoves/:id/:format? |
Get Ranking | GET | /ranking/:order? |
Get Stats | GET | /stats/:email |
Token Balance | GET | /tokenBalance |
Set Tokens | POST | /token |
In the following section it will be explained in detail how to interact with the system through the routes mentioned above. It is crucial to understand that all the routes are preceeded by a JWToken authentication head; this means that without one of this tokens the user cannot do any action. The JWT for this project have been generated using https://jwt.io/ (A service which allows to obtain JWT from payload claims and a secret key).
The secret key used to generate tokens for this project can be found in the .env file; on the website linked above the claims must me indicated as follows:
Once the token has been obtained, in order to use the software, it has to be passed in the HTTP Request Authentication head as 'Bearer Token'.
The game is created by passing in the POST request's body a JSON containing the grid dimension and the opponent's email.
For all POST requests is also important to set the 'Content-Type' parameter to 'application/json'.
The message payload must be formatted like the example below.
This route allows the user to move a pawn in the game he/her is playing (if the user is not in game an error is returned) by indicating the array of moves that the pawn has to do in order to reach its final destination. All moves done by the pawn are verified and if some of them violate one or more game rules an error is returned.
The game creator always controlls the white pawns while the opponent has the black ones. Pawn names are always composed by two parts: ('w'/'b') + (pawn number from creator's left to his right); for example the black pawn to the far left (from the creator point of view) is named b1.
As said above this route allows the user to obtain general informaion about a specific game, which is indicated by an id parameter passed by query string. In the example below we are retriving information about the game identified by id 20.
If the user is currently playing in a game, the latter will be terminated, a loss will be given to the user and a win is attributed to the opponent. If the user is not playing any game an error is returned.
As for the game state route, here the game id is passed through query string in the request URL; in addition to this, if the user indicates as second parameter 'csv', the moves list is returned as a csv string.
As in the previous cases, the parameters are passed as query string, this time the attribute indicates whether the user wants the list to be sorted in ascending order (order = 'asc') or in descending order (order = anything else).
This time the query string parameter is the email of the player whose statistics the user wants to get.
As said before, in order to use this route it's not necessary for the user's token credit to be greater than 0.
This is the only route accessible exclusively by the administrator; it allows to set the token balance of any player to any amount.
The MVC pattern helps us break up the frontend and backend code into separate components. This way, it's much easier to manage and make changes to either side without them interfering with each other. It is made up of 3 components:
- Model : The model's job is to simply manage the data. Whether the data is from a database, API, or a JSON object, the model is responsible for managing it.
- Views : The view's job is to decide what the user will see on their screen, and how.
- Controller : The controller's responsibility is to pull, modify, and provide data to the user. Essentially, the controller is the link between the view and model.
In our case, since the View component is represented only by the routes, we didn't have to worry about the notorious problem related to the View-Model relation.
This pattern chains receiving objects together, and then passes any request messages from object to object until it reaches an object capable of handling the message. The number and type of handler objects isn't known a priori, they can be configured dynamically. The chaining mechanism uses recursive composition to allow an unlimited number of handlers to be linked.
Chain of Responsibility simplifies object interconnections. Instead of senders and receivers maintaining references to all candidate receivers, each sender keeps a single reference to the head of the chain, and each receiver keeps a single reference to its immediate successor in the chain.
In our case, sequential objects are middleware functions that have access to the requested object (req), the response object (res), and the subsequent middleware function in the request-response application loop. The next middleware function is commonly indicated by a variable called next.
The factory design pattern is used when we have a superclass with multiple sub-classes and based on input, we need to return one of the sub-class. This pattern takes out the responsibility of the instantiation of a class from the client program to the factory class.
In our case we used the factory to instantiate the different log message objects.
Singleton is a creational design pattern that lets you ensure that a class has only one instance, while providing a global access point to this instance. The Singleton pattern solves two problems at the same time:
- Ensure that a class has just a single instance.
- Provide a global access point to that instance.
In our case we used the singleton pattern to handle the access to the shared database (which otherwise could be modified by different objects at the same time)
DAO stands for Data Access Object. DAO Design Pattern is used to separate the data persistence logic in a separate layer. This way, the service remains completely in dark about how the low-level operations to access the database is done.
In our case we used this pattern to separate the User related operations from the Game related operations (both done on the same DB).
- First of all, clone the git repository in order to obtain all files needed.
git clone https://github.com/Alessandrob99/AP_Project
- Once the files have been retrived, open a new terminal in the project's root folder and run the command:
docker-compose up
Note : If you are using windows' version of Docker don't forget to launch Docker Desktop first.
-
By default the docker container is reachable via http://0.0.0.0:8080/, but this can be changed by editing the .env file containing all the different parameters.
- DB_NAME : Name of the database containing all the app data.
- DB_HOST : Identifier of the container related to the node app.
- DB_USER : Name of the user account used to access the mysql service.
- DB_PASS : Password of the db user profile.
- DB_PORT : Port which the DB is listening through.
- HOST : Codebase container address
- PORT: Port which the node app container is listening through.
- EXT_PORT : Port related to the Docker app container, mapped to the internal port (PORT)
-
At this point we are ready to use all the routes; an API platform for handling HTTP Requests (like Postman) could be really useful. The same results can be obtained using the CLI, but the whole process would result much more cumbersome.
The project's root folder contains a JSON file with all the tests done in Postman.
In order to take down the service and the Docker container use:
docker-compose down
π¨βπ Bedetta Alessandro