SERVER SIDE MODULE
Contents
MODULE_SERVER_SIDE.docx
MODULE_SERVER_SIDE_MEDIA.zip
Introduction
A new founded company is looking for full stack developers to create an online browser gaming platform. Game developers can upload their games to the platform and users can play them online in the browser.
There are three parts to the platform:
Developer Portal: A web application for game developers to upload their games to the platform.
Administrator Portal: A web application for administrators to manage the platform with its users and games.
Gaming Portal: A web application for players to play games online in the browser.
The company wants to create a minimum viable product (MVP) for the platform. The MVP should already contain the aforementioned parts, but it is acceptable that the Game Developer Portal and the Administrator.
Portal are not fleshed out yet. The Gaming Portal should be fully functional, so that users can play games online in the browser.
Description of Projects
The project is split into two phases:
Phase one for building the API and static pages using a PHP framework and MySQL database.
Phase two for building the frontend parts using HTML/CSS and a JavaScript framework, consuming the API developed in phase one.
You can find the provided media files to support your work:
Provided frameworks (laravel, vuejs, reactjs)
Postman collection and environment
Template GUI (to build frontend UI)
lks-server.sql (a database with structures and dummy data)
Phase 1 : RESTful API
In this phase, you should build a RESTful API using the Laravel framework according to the documentation below.
Ensure users can login using credentials below:
Administrator
Username
Password
admin1
hellouniverse1!
admin2
hellouniverse2!
Developer
Username
Password
dev1
hellobyte1!
dev2
hellobyte2!
Players
Username
Password
player1
helloworld1!
player2
helloworld2!
REST API Specification
General information:
The response bodies contain some static example data. Dynamic data from the database should be used.
Placeholder parameters in the URL are marked with a preceding colon (e.g. :slug or :id).
The order of properties in objects does not matter, but the order in an arrays does.
The Content-Type header of a request must always be application/json for POST, PUT, PATCH.
The Content-Type header of a response is always application/json unless specified otherwise.
Timestamps are formatted as ISO-8601 strings. E.g. 2032-01-31T21:59:35.000Z.
The given URLs are relative to the base URL of the API. E.g. /api/v1/games is the URL to get all games.
The API URLs must not end in .php or .html or any other file extension. The game files are an exception to this.
The token for protected endpoints must be specified as a Bearer token in the `Authorization` header. I.e. `Authorization: Bearer <token>`
1. Authentication
You should create Login and Logout endpoints. The accessToken must be generated by sanctum and will be placed in the request headers Authorization Bearer.
Sign Up
POST [domain]/api/v1/auth/signup
This endpoint creates a new user and returns a session token.
Request Body:
{
"username": "testuser",
"password": "asdf1234"
}
PROPERTY
COMMENT
username
required, unique, min length 4, max length 60
password
required, min length 5, max length 20
Response:
Successful creation response:
Status Code: 201
Response Body:
{
"status": "success",
"token": "xxx"
}
Sign In
POST [domain]/api/v1/auth/signin
This checks the username and password against all known users. If found, a session token is returned.
Valid response:
Status Code: 200
Request Body:
{
"username": "testuser",
"password": "asdf1234"
}
PROPERTY
DESCRIPTION
username
required, unique, min length 4, max length 60
password
required, min length 5, max length 20
Response Body:
{
"status": "success",
"token": "xxx",
“role”: “admin” | “user”
}
Wrong username / password response:
Status Code: 401
Response Body:
{
"status": "invalid",
"message": "Wrong username or password"
}
Sign Out
POST [domain]/api/v1/auth/signout
Deletes the current session token.
Valid response:
Status Code: 200
Response Body:
{
"status": "success"
}
2. Users
Get all admin data
GET [domain]/api/v1/admins
Returns an admin data.
Response:
Status Code: 200
{
"totalElements": 2,
"content": [
{
"username": "admin1",
"last_login_at": "",
"created_at": "2024-04-05 20:55:40",
"updated_at": "2024-04-05 20:55:40",
},
{
"username": "admin2",
"last_login_at": "2024-04-05 20:55:40",
"created_at": "2024-04-05 20:55:40",
"updated_at": "2024-04-05 20:55:40",
}
]
}
User is not administrator response:
Status Code: 403
Response Body:
{
"status": "forbidden",
"message": "You are not the administrator"
}
POST [domain]/api/v1/users
This endpoint can be used to create a user.
Request Body:
{
"username": "testuser",
"password": "asdf1234"
}
PROPERTY
COMMENT
username
required, unique, min length 4, max length 60
password
required, min length 5, max length 20
Response:
Successful creation response:
Status Code: 201
Response Body:
{
"status": "success",
"username": "testuser"
}
Existing username:
If the username is not unique, the admin user cannot be created and instead the following response is returned.
Response:
Status Code: 400
{
"status": "invalid",
"message": "Username already exists"
}
User is not administrator response:
Status Code: 403
Response Body:
{
"status": "forbidden",
"message": "You are not the administrator"
}
GET [domain]/api/v1/users
Returns a user details.
Response:
Status Code: 200
{
"totalElements": 2,
"content": [
{
"username": "player1",
"last_login_at": "",
"created_at": "2024-04-05 20:55:40",
"updated_at": "2024-04-05 20:55:40",
},
{
"username": "player2",
"last_login_at": "2024-04-05 20:55:40",
"created_at": "2024-04-05 20:55:40",
"updated_at": "2024-04-05 20:55:40",
}
]
}
User is not administrator response:
Status Code: 403
Response Body:
{
"status": "forbidden",
"message": "You are not the administrator"
}
PUT [domain]/api/v1/users/:id
This endpoint can be used to update a user.
Request Body:
{
"username": "testuser",
"password": "asdf1234"
}
PROPERTY
COMMENT
username
required, unique, min length 4, max length 60
password
required, min length 5, max length 20
Response:
Successful creation response:
Status Code: 201
Response Body:
{
"status": "success",
"username": "testuser"
}
Existing username:
If the username is not unique, the admin user cannot be created and instead the following response is returned.
Response:
Status Code: 400
{
"status": "invalid",
" message": "Username already exists"
}
User is not administrator response:
Status Code: 403
Response Body:
{
"status": "forbidden",
"message": "You are not the administrator"
}
DELETE [domain]/api/v1/users/:id
This endpoint can be used to update a user.
Successful deletion response:
Status Code: 204
This returns an empty body. The `Content-Type` header does not have to be `application/json`.
User is not found response:
Status Code: 403
Response Body:
{
"status": "not-found",
"message": "User Not found"
}
User is not administrator response:
Status Code: 403
Response Body:
{
"status": "forbidden",
"message": "You are not the administrator"
}
3. Games
GET [domain]/api/v1/games
Returns a paginated list of games.
Query Parameters:
PROPERTY
DESCRIPTION
DEFAULT
page
Page number. Starts at 0
0
size
Page size. Must be greater or equal than 1
10
sortBy
Field to sort by. Must be one of "title", "popular", "uploaddate"
title
sortDir
Describes sort direction. Must be one of "asc" or "desc"
asc
The sort fields are explained here:
FIELD
DESCRIPTION
title
Game title
popular
Counts the total number of scores per game and sorts by this count
uploaddate
Latest game version upload timestamp
In `content`, the fields `thumbnail` and `uploadTimestamp` refer only to the latest version.
The field `scoreCount` is the sum of scores over all versions.
Response:
Status Code: 200
Response Body:
{
"page": 0,
"size": 10,
"totalElements": 15,
"content": [
{
"slug": "demo-game-1",
"title": "Demo Game 1",
"description": "This is demo game 1",
"thumbnail": "/games/:slug/:version/thumbnail.png",
"uploadTimestamp": "2032-01-31T21:59:35.000Z",
"author": "dev1",
"scoreCount": 5
}
]
}
Response page fields explained:
FIELD
DESCRIPTION
page
The requested page number. Starts at 0
size
The actual page size. Must be less or equal than the requested page size
totalElements
The total number of elements regardless of page
content
An array of the games in the page
It can be computed how many pages there are:
pageCount = ceil(totalElements / requestedSize)
It can also be computed if the returned page is the last page by multiplying the (page+1) by requested page size and checking if the result is less than or equal to the total elements.
isLastPage = (page + 1) * requestedSize >= totalElements
Note 1: If there is a game that has no game version yet, it is not included in the response nor the total count.
Note 2: If there is no thumbnail, the thumbnail field is null.
POST [domain]/api/v1/games
This endpoint can be used to create a game. However, the game version needs to be uploaded in a separate step. If a game does not have a version yet, it is not returned in this endpoint.
Request Body:
{
"title": "Demo Game 3",
"description": "This is demo game 3"
}
PROPERTY
DESCRIPTION
title
required, min length 3, max length 60
description
required, min length 0, max length 200
Response:
Successful creation response:
Status Code: 201
Response Body:
{
"status": "success",
"slug": "generated-game-slug"
}
Existing slug:
If the generated slug is not unique, the game cannot be created and instead the following response is returned.
Response:
Status Code: 400
{
"status": "invalid",
"slug": "Game title already exists"
}
GET [domain]/api/v1/games/:slug
Returns a games details.
Response:
Status Code: 200
{
"slug": "demo-game-1",
"title": "Demo Game 1",
"description": "This is demo game 1",
"thumbnail": "/games/:slug/:version/thumbnail.png",
"uploadTimestamp": "2032-01-31T21:59:35.000Z",
"author": "dev1",
"scoreCount": 5,
"gamePath": "/games/demo-game-1/1/"
}
If there is no thumbnail, the thumbnail field is null.
The `gamePath` field points to a URL path that browsers can use to render the game. This means this is a reachable asset path.
Game file upload
POST [domain]/api/v1/games/:slug/upload
The user can upload a new version of a game if they are the author of that game.
This is not a REST endpoint and rather it accepts a file upload. The parameter name is `zipfile`.
The version of the game is an integer and incrementing. The first version is `1`. The user cannot control the version.
The session token needs to be provided as form parameter `token`.
The path has to be stored in the game record, so that players can find the game files.
If the upload fails because of one of these possible reasons, the response must be a plain text explanation of the error.
User is not author of the game
Serve game files
GET /games/:slug/:version/
The game files that were uploaded are served under that path which is public.
PUT [domain]/api/v1/games/:slug
This endpoint allows the author of the game to update the game title and description.
Request Body:
{
"title": "Demo Game 1 (updated)",
"Des cription": "Updated description"
}
Note: This does not update the game's slug.
Response:
Successful update response:
Status Code: 200
Response Body:
{
"status": "success"
}
User is not game author response:
Status Code: 403
Response Body:
{
"status": "forbidden",
"message": "You are not the game author"
}
DELETE [domain]/api/v1/games/:slug
The author can delete their game. This deletes the game, all versions and all scores.
Response:
Successful deletion response:
Status Code: 204
This returns an empty body. The `Content-Type` header does not have to be `application/json`.
User is not game author response:
Status Code: 403
Response Body:
{
"status": "forbidden",
"message": "You are not the game author"
}
GET [domain]/api/v1/users/:username
Returns the user details.
Response:
Status Code: 200
Response Body:
{
"username": "dev1",
"registeredTimestamp": "2032-01-31T21:59:35.000Z",
"authoredGames": [
{
"slug": "demo-game-1",
"title": "Demo Game 1",
"description": "This is demo game 1"
}
],
"highscores": [
{
"game": {
"slug": "demo-game-1",
"title": "Demo Game 1",
"description": "This is demo game 1"
},
"score": 15,
"timestamp": "2032-01-31T21:59:35.000Z"
}
]
}
The authoredGames is an array that returns all games with at least one version where this user is the author. If the user requesting the user details is the user itself, this returns also games that have no version yet.
The highscores is an array of highest scores per game played.
GET [domain]/api/v1/games/:slug/scores
Returns the highest scores of each player that played any version of the game, sorted by score (descending).
Response:
Status Code: 200
Response Body:
{
"scores": [
{
"username": "player2",
"score": 20,
"timestamp": "2032-01-31T21:59:35.000Z"
},
{
"username": "player1",
"score": 15,
"timestamp": "2032-01-31T21:59:35.000Z"
}
]
}
POST [domain]/api/v1/games/:slug/scores
When a user ends a game run, the score can be posted to this endpoint.
Request Body:
{
"score": 100
}
The game version associated to the score is the latest one available.
Response:
Successful creation response:
Status Code: 201
Response Body:
{
"status": "success"
}
Invalid request body
If the POST or PUT request had invalid fields, they are validated and a response is returned that lists the violations.
Status Code: 400
Response Body:
{
"status": "invalid",
"message": "Request body is not valid.",
"violations": {
"field_name": {
"message": "required"
},
"field_name": {
"message": "must be at least 4 characters long"
},
"field_name": {
"message": "must be at most 60 characters long"
}
}
}
In the above example, all possible violations are shown. The actual returned violations should only be the fields which were actually invalid. At most one validation per field is shown. Validations are executed in order of appearance in the example above. field_name must be replaced with the actual field name.
The messages for length must include the actual length requirement value.
Missing or invalid auth header
If the consumer makes a call to a path that requires the auth header to be present, this must be the response:
Status Code: 401
Response Body:
{
"status": "unauthenticated",
"message": "Missing token"
}
If the consumer makes a call to a path that requires the auth header to be present, but provides an invalid or not existing token, this must be the response:
Status Code: 401
Response Body:
{
"status": "unauthenticated",
"message": "Invalid token"
}
If the consumer makes a call to a path that requires the auth header to be present, but the user is blocked, this must be the response:
Status Code: 403
Response Body:
{
"status": "blocked",
"message": "User blocked",
"reason": "You have been blocked by an administrator"
}
Note: The reason is a dynamic value, chosen by the admin that blocks the user.
The following method and path patterns require a valid session header to be present:
POST /api/v1/auth/signout
POST, PUT, DELETE /api/v1/games/**
GET, POST, PUT, DELETE /api/v1/users/**
GET /api/v1/admins/**
Non-existing API path
If the consumer makes a call to a non-existing path, or a resource that does not exist, this must be the response:
Status Code: 404
Response Body:
{
"status": "not-found",
"message": "Not found"
}
Phase 2 : Front-end Development
In this part, you should build a front-end application using one of the provided frameworks (vue js or react js). You can use the provided template gui on the media files to build the front-end ui.
Pages / Features
Description
Sign Up,
Sign In,
Sign Out
User can sign in using the correct credentials (username and password)
Alert errors should be displayed when login failed
Users can sign out by clicking the logout button on the navbar.
2 level user can login (administrator and user)
Administrator menu (List Admin, List User)
User menu (Discover Games, Manage Games, User Profile)
Home
Users can see their login info.
List Admin
The admin can see all admin users in the database with username, created timestamp and last login timestamp.
List User
The admin can see all platform users with username, registration timestamp, last login timestamp and click a link to their profile page
The admin can block and unblock users
The admin can create, update and delete users
Discover games
See a list of games. For each game, the following is shown:
Title
Score Count: Number of scores submitted
Description: The description provided by the author.
Thumbnail: If available, otherwise a default graphic.
The list can be sorted by popularity, recently updated, or title. Both ascending and descending options can be selected by the user.
The link to the game should have link-only link purpose applied for accessibility
The games are shown in an infinitely scrolling list. With an infinitely scrolling list, there are no pages, but scrolling the page triggers loading of more games if the scroll position gets close to the end of the currently loaded games. In infinite scroll mode, there are no pages, but scrolling triggers loading of more games.
Detail Game
This page renders the game shows the current highscores (which are automatically updated).
If the user is within the top ten, the current user is highlighted and marked as being the current user (for example by showing it bold). If the current user has a score that ranks below the top ten, then the score is appended, but shown without rank.
The game description is shown as well.
User Profile
This page shows the user profile with:
Username
Authored Games
The list is sorted by when the last version was uploaded. The most recently updated game is at the top.
If the user has not uploaded any games, the "Authored Games" section is omitted.
Per authored game, the following is shown: Game Title, Description, Thumbnail, The number of submitted scores
The author is not shown per game as this is redundant information.
Highscores
The list shows the highest score per game.
The list is sorted by game title alphabetically.
Each game has a link that can be clicked to go to the game.
Manage Game
On this page, developers can update title and description of a game, delete the game, or upload a new version. On this page, developers can do the following:
Update title and description.
Upload a new version. This requires the user to upload a ZIP file.
Delete the game. The game is only deleted after the user confirms.
If a user tries to reach this page without being the author, they are redirected to the game itself.
ERD
Instructions
Save your REST Api in the directory called “SERVER_MODULE/backend” and save it in the repository folder.
Db-dump.png sama db-diagram.pdf
Save your front-end app in the directory called “SERVER_MODULE/frontend” and save it in the repository folder.
When developing the Frontend, you can use/consume the provided REST API.
You should build the frontend app to production mode and save the production files in the “SERVER_MODULE/frontend/build” folder before you push it to the github repository.
Make sure the “/build” folder isn't written on the .gitgnore file.
You should commit and push your changes to the github repository at least every 30 minutes.