The task requires .NET 8 SDK installed. You can find project configuration requerarments in this file
System should support the following features:
- Get game by key.
- Get games by genre.
- Get games by platform.
- Download a game file.
- CRUD for games.
- CRUD for genres.
- CRUD for platforms.
- Global error handling.
Technical specifications:
- Use the latest stable version of ASP.NET CORE (MVC template).
- N-layer architecture should be used.
- Use a built-in service provider.
- Use SOLID principles.
- Use JSON as response/request format.
- Use MS SQL Server.
Game:
- Id: Guid, required, unique
- Name: String, required
- Key: String, required, unique
- Description: String, optional
Genre:
- Id: Guid, required, unique
- Name: String, required, unique
- ParentGenreId: Guid, optional
Platform:
- Id: Guid, required, unique
- Type: String, required, unique
GameGenre:
- GameId: Guid, required.
- GenreId: Guid, required.
Game-Genre combinations are unique.
GamePlatform:
- GameId: Guid, required.
- PlatformId: Guid, required
Game-Platform combinations are unique.
Game key It is a short unique name of the game that can be used for creating user-friendly routes The key can be entered manually. If the key is not entered manually – then it must be generated automatically based on the game name. Game File It is an autogenerated .txt file with “_” prefix in the name and serialized game as content. Predefined Genres The system should contain predefined genres and subgenres: Strategy RTS TBS RPG Sports Races Rally Arcade Formula Off-road Action FPS TPS Adventure Puzzle & Skill
The system should contain predefined platforms:
- Mobile
- Browser
- Desktop
- Console
Please use the following CI with pipelines, quality gate by code style, and unit-tests coverage for your project:
CI.zip
Add a new Game endpoint
Url: /games
Type: POST
Request Example:
{
"game": {
"name": "Super game name",
"key": "gameKey",
"description": "Some text"
},
"genres": [
"7c9c7b90-363f-487a-bc08-0e9a31e60d2e",
"acd05e04-a8df-4d6f-9369-b6d6bd40e1ee"
],
"platforms": [
"34df8f15-ca69-4c44-949a-f53ca4929a72"
]
}
Get game by key endpoint
Url: /games/{key}
Type: GET
Response example:
{
"id": "15d9da6c-1f12-483d-b4ea-97f661c2ce0e",
"description": "Some text",
"key": "gameKey",
"name": "Super game name"
}
Get game by id endpoint
Url: /games/find/{id}
Type: GET
Response example:
{
"id": "0146978c-cf50-45cd-8949-605938f920a9",
"description": "string",
"key": "string",
"name": "string"
}
Get games by platform endpoint
Url: /platforms/{id}/games
Type: GET
Response example:
[
{
"id": "777d5792-3602-423a-8e8d-272a9a22b0fc",
"description": null,
"key": "Game1",
"name": "Game1"
},
{
"id": "4883afcf-3c66-4d44-a622-b8d5d6ced8df",
"description": null,
"key": "Game2",
"name": "Game2"
}
]
Get games by genre endpoint
Url: /genres/{id}/games
Type: GET
Response example:
[
{
"id": "777d5792-3602-423a-8e8d-272a9a22b0fc",
"description": null,
"key": "Game1",
"name": "Game1"
},
{
"id": "4883afcf-3c66-4d44-a622-b8d5d6ced8df",
"description": null,
"key": "Game2",
"name": "Game2"
}
]
Update a Game endpoint
Url: /games
Type: PUT
Request example:
{
"game": {
"name": "New name",
"key": "newkey",
"description": "Updated desc",
"id": "15d9da6c-1f12-483d-b4ea-97f661c2ce0e"
},
"genres": [
"15d9da6c-1f12-483d-b4ea-97f661c2ce0e"
],
"platforms": [
"61bf29f3-d7f0-4afe-a283-f4c7d8865d96"
]
}
Delete a Game endpoint
Url: /games/{key}
Type: DELETE
Download a Game endpoint
Url: /games/{key}/file
Type: GET
Result: file downloading is started
Get All games endpoint
Url: /gamesType: GET
Response example:
[
{
"id": "0146978c-cf50-45cd-8949-605938f920a9",
"description": "string",
"key": "string",
"name": "string"
},
{
"id": "777d5792-3602-423a-8e8d-272a9a22b0fc",
"description": null,
"key": "Game1",
"name": "Game1"
},
{
"id": "4883afcf-3c66-4d44-a622-b8d5d6ced8df",
"description": null,
"key": "Game2",
"name": "Game2"
},
{
"id": "15d9da6c-1f12-483d-b4ea-97f661c2ce0e",
"description": "Updated desc",
"key": "newkey",
"name": "New name"
}
]
Add a new Genre endpoint
Url: /genres
Type: POST
Request example:
{
"genre": {
"name": "Sub genre",
"parentGenreId": "ed1ecab9-358a-4c19-9ab7-6031bda097a9"
}
}
Get genre by id endpoint
Url: /genres/{Id}
Type: GET
Response Example:
{
"id": "f668ed18-b5f5-41df-b48d-a9ec6451d55d",
"name": "genre name 2",
"parentGenreId": null
}
Get all genres endpoint
Url: /genres
Type: GET
Response Example:
[
{
"id": "ed1ecab9-358a-4c19-9ab7-6031bda097a9",
"name": "string"
},
{
"id": "334b000f-0f8b-417f-b7da-e4549f0f0518",
"name": "genre name"
},
{
"id": "f668ed18-b5f5-41df-b48d-a9ec6451d55d",
"name": "genre name 2"
},
{
"id": "2420ef87-c5ff-4354-91e7-b768a4ec3e8b",
"name": "Sub genre"
}
]
Get genres by game key endpoint
Url: /games/{key}/genres
Type: GET
Response Example:
[
{
"id": "ed1ecab9-358a-4c19-9ab7-6031bda097a9",
"name": "string"
}
]
Get genres by parent id endpoint
Url: /genres/{id}/genres
Type: GET
Response Example:
[
{
"id": "2420ef87-c5ff-4354-91e7-b768a4ec3e8b",
"name": "Sub genre"
}
]
Update a Genre endpoint
Url: /genres
Type: PUT
Request Example:
{
"genre": {
"id": "ed1ecab9-358a-4c19-9ab7-6031bda097a9",
"name": "Updated Name",
"parentGenreId": null
}
}
Delete a Genre
Url: /genres/{Id}
Type: Delete
Create a new Platform endpoint
Url: /platforms
Type: POST
Request example:
{
"platform": {
"type": "Platform Name"
}
}
Get platform by id endpoint
Url: /platforms/{Id}
Type: GET
Response Example:
{
"id": "b763be02-652c-4d89-9a65-ec1f1c4106f2",
"type": "Updated Name"
}
Get all platforms endpoint
Url: /platforms
Type: GET
Response Example:
[
{
"id": "61bf29f3-d7f0-4afe-a283-f4c7d8865d96",
"type": "string"
},
{
"id": "fdd7dae2-76d5-4ee6-ac97-3d21c3d0b41c",
"type": "Platform Name"
},
{
"id": "b763be02-652c-4d89-9a65-ec1f1c4106f2",
"type": "Updated Name"
}
]
Get platforms by game key endpoint
Url: /games/{key}/platforms
Type: GET
Response Example:
[
{
"id": "61bf29f3-d7f0-4afe-a283-f4c7d8865d96",
"type": "string"
}
]
Update a Platform endpoint
Url: /platforms
Type: PUT
Request example:
{
"platform": {
"id": "b763be02-652c-4d89-9a65-ec1f1c4106f2",
"type": "Updated Name"
}
}
Delete a Platform endpoint
Url: /platforms/{Id}
Type: DELETE
Implement global exception handling
NFR1E1
Repository and Unit of Work patterns should be implemented.
NFR2E1
Use the latest stable version of Microsoft Entity framework Core.
NFR3E1
Add GET endpoints responses caching for 1 minute.
Implement the GetAccountOwnersTotalBalance method
Please use the following CI with pipelines, quality gate by code style, and unit-tests coverage for your project:
CI.zip
System should support the following features:
- Total Games count calculation.
Technical specifications:
- Code coverage rate must be not lower than 50%.
- Use Moq package for test data mocks.
- Use XUnit package for unit tests.
Add x-total-numbers-of-games response header which represents the total games count in the system. A header should be added in each API response. Hint: The header should be exposed in Cors’s configuration. Example:
content-type: application/json; charset=utf-8
date: Fri,17 Nov 2023 18:21:31 GMT
server: Kestrel
x-total-numbers-of-games: 4
x-total-numbers-of-games header value should be cached for 1 minute
E02 NFR1 Cover application code with by Unit tests with coverage not lower than 50%.
System should support the following features:
- Inbound requests logging.
- Error logging.
- Swagger.
Technical specifications:
- Use build-in logging infrastructure.
- Use a global exception handler for error logging.
- .txt files should be used as log storage.
- A separate log file per day should be used.
Add Swagger infrastructure. All endpoints should be accessible via the Swagger page.
Create user-friendly response message in case of error.
**E03 NFR1 ** Create a middleware for logging request details, logs should include the user IP Address, target URL, response status code, request content, response content, and elapsed time.
E03 NFR2 Add exception details logging, logs should include the exception type, exception message, inner exceptions, exception details, stack trace.
E03 NFR3 Implement a logging in file to store all logs produced by the application. Use a separate file for exception logs.
E03 NFR4[optional] Add logs in main business layer methods.
Extend the functionality of the Game Store by adding the ability to buy a game.
Please use the following Angular Front-end: gamestore-ui-app
You should use the following Microservice payment
System should support the following features:
- Add game to cart.
- Get games from cart.
- Get orders.
- Get payment methods.
- Perform payment with the selected method.
Order
- Id: Guid, required, unique
- Date: DateTime, optional
- CustomerId: Guid, required
- Status: Enum, required
OrderGame
- OrderId: Guid, required
- ProductId: Guid, required
- Price: Double, required
- Quantity: Int, required
- Discount: Int, optional
Product-Order combinations are unique.
Create a single endpoint for payment independently from the selected method.
If the payment is processed successfully then the order must be marked as paid otherwise cancelled
Allowed payment methods: “Bank”, “IBox terminal”, “Visa”.
Only one method can be applied to an order to get paid.
Each payment method contains a little picture (open-source pictures), title, short description.
Since we don’t have users yet, for Customer Id use a stub value.
The date of validity – how long the invoice is valid. The value should be configurable by application settings.
User cannot order more games than available in the stock.
Any order has the next possible statuses:
- Open – games are in the cart.
- Checkout – payment is started.
- Paid – payment is performed successfully.
- Cancelled – payment is performed with errors.
Add game in and delete from the cart Add game in the cart endpoint.
Url: /games/{key}/buy
Type: POST
Limitation: if the endpoint is called for the game which is already in the cart, then just increment the quantity
Response: Success status code
Delete game from cart endpoint
Url: /orders/cart/{key}
Type: DELETE
Response: Success status code
Get paid and cancelled orders endpoint.
Url: /orders
Type: GET
Response example:
[
{
"id": "5d8af81a-c146-4588-93bf-0e5c7e9e4a9e",
"customerId": "5aa1c97e-e6b3-497c-8e00-270e96aa0b63",
"date": "2023-11-20T11:03:26.0572863+02:00"
},
{
"id": "bec0ffb1-fb80-47ff-8720-2f9a544a55a2",
"customerId": "bfa70dfa-6b18-4f3f-b882-14af597396d4",
"date": "2023-11-18T11:03:26.0575052+02:00"
}
]
Get order by id endpoint.
Url: /orders/{id}
Type: GET
Response example:
{
"id": "5d8af81a-c146-4588-93bf-0e5c7e9e4a9e",
"customerId": "5aa1c97e-e6b3-497c-8e00-270e96aa0b63",
"date": "2023-11-20T11:03:26.0572863+02:00"
}
Get order details endpoint.
Url: /orders/{id}/details
Type: GET
Response Example:
[
{
"productId": "8ea595c2-765b-4190-b3da-da00540b2202",
"price": 100,
"quantity": 3,
"discount": 0
},
{
"productId": "c3a73173-fe0f-4771-a7eb-f2cd458d8303",
"price": 10,
"quantity": 5,
"discount": 0
},
{
"productId": "923a8bd8-b256-44f4-b972-a0d640d56ef4",
"price": 100,
"quantity": 1,
"discount": 10
}
]
Get cart endpoint.
Url: /orders/cart
Type: GET
Hint: Cart is an order in the status Open, if such an order is not present in the database this should be created automatically during adding the first game to the cart. As a result, if the last game is deleted from the cart then the open order should be deleted automatically.
Response example:
[
{
"productId": "f6f698ee-41df-4594-b90c-3862e7d2cbee",
"price": 30,
"quantity": 1,
"discount": 0
},
{
"productId": "75396383-c1fa-4cbd-81c9-c874ae3a3e67",
"price": 100,
"quantity": 1,
"discount": 20
}
]
Get payment methods endpoint.
Url: /orders/payment-methods
Type: GET
Response Example:
{
"paymentMethods": [
{
"imageUrl": "image link1",
"title": "Bank",
"description": "Some text 1"
},
{
"imageUrl": "image link2",
"title": "IBox terminal",
"description": "Some text 2"
},
{
"imageUrl": "image link3",
"title": "Visa",
"description": "Some text 3"
}
]
}
"Bank" payment
Url: /orders/payment
Type: POST
Request: {"method":"Bank"}
Flow: the system should return generated invoice file for download:
File type: PDF
The file content:
User ID
Order ID
Creation date
The date of validity – how long is the invoice is valid.
Sum
"IBox terminal" payment
Url: /orders/payment
Type: POST
Request: {"method":"IBox terminal"}
Integration: Integration with payment microservice is required
Flow: The system should handle requests with an IBox payment.
Response: should contain a user Id, invoice number (order ID), and sum.
Response Example:
{
"userId": "24967e32-dec1-47b5-8ca6-478afa84c2be",
"orderId": "7dce8347-4181-4316-9210-302361340975",
"paymentDate": "2023-11-18T11:03:26.0575052+02:00",
"sum": 100
}
"Visa" payment
Url: /orders/payment
Type: POST
Request:
{
"method": "Visa",
"model": {
"holder": "Vitalii",
"cardNumber": "123321122344231",
"monthExpire": 10,
"yearExpire": 2030,
"cvv2": 111
}
}
Integration: Integration with payment microservice is required
Flow: The system should handle requests with card holder’s name, card number, Date of expiry (month and year), CVV2/CVC2
Response: Success status code.
E05 NFR1
Microservice - an additional project that must be run locally and integrated with the game store api for Visa and IBox payments.
You can investigate possible requests and results of microservice by calling the swagger endpoint.
E05 NFR2
Implement full tolerance payment acceptation.
As the microservice that accepts iBox and card payments can reject up to 10 % of the transactions.
A response validation must be implemented and an additional request must be sent in case of failure, the solution must be able to work with all responses of the Microservice.
Please use the following Angular Front-end: gamestore-ui-app
Existing Authorization microservice
System should support the following features:
- User Management
- Login
- Roles Management
- Access by permissions
- The system should have the next default roles: Administrator, Manager, Moderator, User, and Guest (not authorized).
- Custom roles can be created additionally.
- Multiple roles can be assigned to the user.
- Can manage users and roles.
- Can see deleted games.
- Can manage comments for the deleted game.
- Can edit a deleted game.
- Can manage business entities: games, genres, publishers, platforms, etc.
- Can edit orders.
- Can view orders history.
- Can’t edit orders from history.
- Can change the status of an order from paid to shipped.
- Can't edit a deleted game.
- Can manage game comments.
- Can ban users from commenting.
- Can`t see deleted games.
- Can’t buy a deleted game.
- Can see the games in stock.
- Can comment game.
- Has read-only access.
- The username should be used as the commenter's name now.
- Order history displays orders older than 30 days by default.
Default Roles at the top inherit all accepts and limitations from roles below if other behavior is not specified in the role description.
Admin => Manager => Moderator => User => Guest
Login endpoint.
Url: /users/login
Type: POST
Request Example:
{
"model": {
"login": "UserName",
"password": "SuperSecuredPassword",
"internalAuth": true
}
}
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}
Check page access endpoint.
Url: /users/access
Type: POST
Request Example:
{
"targetPage": "Genre",
"targetId": "84ea3383-08c2-48ba-9866-34c7e08e6e61"
}
Get all users endpoint.
Url: /users
Type: GET
Response Example:
[
{
"name": "Vitalii",
"id": "454d4d01-406b-4a9b-9f8c-3fec63fc9266"
},
{
"name": "John",
"id": "80fbf934-45e7-49a8-8ae2-868a70bed2bf"
}
]
Get user by id endpoint.
Url: /users/{id}
Type: GET
Response Example:
{
"name": "Vitalii",
"id": "a997674c-d34d-4074-81d2-fe27d739f55a"
}
Delete user by id endpoint.
Url: /users/{id}
Type: DELETE
Get all roles endpoint.
Url: /roles
Type: GET
Response Example:
[
{
"name": "Admin",
"id": "529e960f-79c9-4e25-b3ef-a5ce8cbb42bc"
},
{
"name": "Manager",
"id": "765f9e20-fb70-4837-8b22-5d280ad9d2d2"
}
]
Get role by id endpoint.
Url: /roles/{id}
Type: GET
Response Example:
{
"name": "Admin",
"id": "529e960f-79c9-4e25-b3ef-a5ce8cbb42bc"
}
Delete role by id endpoint.
Url: /roles/{id}
Type: DELETE
Add user endpoint.
Url: /users
Type: POST
Request Example:
{
"user": {
"name": "test"
},
"roles": [
"529e960f-79c9-4e25-b3ef-a5ce8cbb42bc",
"765f9e20-fb70-4837-8b22-5d280ad9d2d2"
],
"password": "testpassword"
}
Update user endpoint.
Url: /users
Type: PUT
Request Example:
{
"user": {
"id": "9109858d-139b-4a13-a212-c3f6cf4ccc78",
"name": "Vitalii"
},
"roles": [
"765f9e20-fb70-4837-8b22-5d280ad9d2d2"
],
"password": "updatedpassword"
}
Get user roles endpoint.
Url: /users/{id}/roles
Type: GET
Response Example:
[
{
"name": "Admin",
"id": "484aeeeb-89d5-4ee7-b8c7-67c7d93292bb"
},
{
"name": "Manager",
"id": "548d6bec-635b-44ec-b719-baaf32758f12"
}
]
Get permissions endpoint.
Url: /roles/permissions
Type: GET
Response Example:
[
"AddGame",
"DeleteGame",
"ViewGame",
"UpdateGame"
]
Get role permissions endpoint.
Url: /roles/{id}/permissions
Type: GET
Response Example:
[
"ViewGame",
"UpdateGame"
]
Add role endpoint.
Url: /roles
Type: POST
Request example:
{
"role": {
"name": "test role"
},
"permissions": [
"AddGame",
"ViewGame",
"UpdateGame"
]
}
Update role endpoint.
Url: /roles
Type: PUT
Request example:
{
"role": {
"id": "73e12b67-8f8e-4df9-bf0d-f1d7cb7296b4",
"name": "User"
},
"permissions": [
"ViewGame"
]
}
Get all games without filters endpoint.
Url: /games/all
Type: GET
Response Example:
[
{
"id": "92753fa3-0207-4fd3-b892-0fafcb23d429",
"description": "Test Desc",
"key": "test1",
"name": "Test Name",
"price": 100,
"discount": 0,
"unitInStock": 100000
},
{
"id": "12649a00-ca90-4d5e-b088-045db9cf4d9c",
"description": "Test Desc 2",
"key": "test2",
"name": "Test Game2",
"price": 10,
"discount": 10,
"unitInStock": 9000
}
]
Update order detail quantity endpoint.
Url: /orders/details/{id}/quantity
Type: PATCH
Request Example:
{
"count": 3
}
Delete order detail endpoint.
Url: /orders/details/{id}
Type: DELETE
Ship order endpoint.
Url: /orders/{id}/ship
Type: POST
Add game as the order detail endpoint.
Url: /orders/{id}/details/{key}
Type: POST
E09 NFR1
Implement claim-based authorization.
E09 NFR2 [Optional]
Implement authentication with external microservice.