-
Ability to create account with two roles (admin, user) and log in.
-
Admin should be able to:
- Add items
- Suspend user
-
User should be able to:
- List available items
- Add items to a cart (if there are items in stock)
- Remove items from their cart
-
Restrict the access to APIs through RBAC mechanism.
-
Accounts ID, Name, Email, Password, Role (Enum), Active(Enum)
-
Items ID, Name, SKU
-
Cart ID, AccountID, ItemID, ItemName
-
Admin
- Add Item Method: POST URI: /api/v1/admin/items Request Body: {'name': 'xyz', 'sku': 5} Headers: {'Authorization': 'Bearer xxxxxxx', 'Content-Type': 'application/json'} Response Codes: 201, 400, 403, 500 Content-Type: application/json
- Suspend User Method: PUT URI: /api/v1/accounts/suspend Headers: {'Authorization': 'Bearer xxxxxxx', 'Content-Type': 'application/json'} Response Code: 200, 400, 403, 500 Request Body: {'email': 'not@allowed.in'}
-
Users
- List Available Items in store Method: GET URI: /api/v1/user/items Headers: {'Authorization': 'Bearer xxxxxxx', 'Content-Type': 'application/json'} Response Body: [{'name': 'xyz', 'sku': 5}, ...] Response Codes: 200, 403, 500
- Add Item to Cart Method: POST URI: /api/v1/user/cart-items Headers: {'Authorization': 'Bearer xxxxxxx', 'Content-Type': 'application/json'} Request Body: {'id': 1, 'name': 'xyz'} Response Codes: 200, 400, 403, 500
- Remove Items from Cart Method: DELETE Headers: {'Authorization': 'Bearer xxxxxxx'} URI: /api/v1/user/cart-items?item_id={x} Response Codes: 204, 400, 403, 500
-
Accounts
-
Create account Method: POST URI: /api/v1/accounts/register Request Body: {'name': 'xss', 'email': 'dasd@sda.com', 'password': 'somethinglong', 'role': 'user'}
Note: allowed
role
values areuser
andadmin
.Response Codes: 201, 400, 500
-
Log In Method: POST URI: /api/v1/accounts/login Request Body: {'username': 'dasd@sda.com', 'password': 'rsewdsa'} Response Codes: 200, 400, 500
-
Log Out Method: POST URI: /api/v1/accounts/logout Headers: {'Authorization': 'Bearer xxxxxxx'} Response Codes: 200, 400, 500
-
- Go version: 1.20
- Postgres
- Create an
application.yaml
file in the root of the project directory. - Copy the following configurations in the
application.yaml
file:APP_PORT: '8888' JWT_REALM: 'test zone' JWT_SECRET: 'secret key' DATABASE_NAME: 'shop' DATABASE_HOST: 'localhost' DATABASE_PORT: '5432' DATABASE_USER: 'owner' DATABASE_PASSWORD: 'secret' DATABASE_DIALECT: 'postgres' DATABASE_SSL_MODE: 'disable' DATABASE_MIGRATIONS_DIR: 'internal/migrations'
- Using any Postgres client: CLI app or a GUI, run the following commands:
CREATE ROLE "owner" with login password 'secret'; CREATE DATABASE shop;
- From the root of the project directory, run the following command:
go run cmd/cli/main.go db:migrate:up
Running the above command will create the necessary tables in the database.
- To finally run the webserver, from the root of the project directory, run the following command:
go run cmd/cli/main.go start:webserver
Note: The examples are showcased using an HTTP client library called
httpie
To installhttpie
, runbrew install httpie
in the terminal.
-
Register Request:
http -v --json POST "localhost:8888/api/v1/accounts/register" name=mayank email=mayank@admin.com password=secretpass role=admin "Content-Type: application/json"
Example Response:
{"message": "account created successfully.", "status": "success"}
-
Login Request:
http -v --json POST localhost:8888/api/v1/accounts/login email=mayank@admin.com password=secretpass
Example Response:
{"code": 200, "expire": "2023-03-13T11:21:51+05:30", "token": "xxxxxx"}
-
Logout Request:
http -v --json POST localhost:8888/api/v1/accounts/logout "Authorization: Bearer xxxx" "Content-Type: application/json"
Response:
{"code": 200}
-
Suspend User Request:
http -v --json PUT "localhost:8888/api/v1/accounts/suspend" email=may@dju.com "Authorization: Bearer xxxx"
Response:
{"message": "account suspended successfully.", "status": "success"}
-
List Items Request:
http -f GET "localhost:8888/api/v1/user/items" "Authorization: Bearer xxxxx"
Example Response:
[{ "name": "chair", "sku": "5" }, { "name": "table", "sku": "5" }, { "name": "amp", "sku": "5" }]
-
Add Item To Cart Request:
http -v --json POST "localhost:8888/api/v1/user/cart-items" item_id=1 "Authorization: Bearer xxxxxx"
Example Response:
{"message": "item added successfully", "status": "success"}
-
Remove Item From Cart Request:
http DELETE "localhost:8888/api/v1/user/cart-items?item_id=1" "Authorization: Bearer xxxxx"
item_id query param refers to the ID of the item in the "items" table.
Example Response: No response. Only 204 No Content response is returned
-
Ping Request:
http GET "localhost:8888/api/ping"
Example Response:
{"ping": "pong"}
-
Refresh Auth Token Request:
http GET "localhost:8888/api/v1/auth/refresh_token" "Authorization: Bearer xxxx"
Example Response:
{"code": 200, "expire": "2023-03-13T16:43:13+05:30", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50X2lkIjoiNyIsImV4cCI6MTY3ODcwNTk5MywiaWQiOiJtYXlAZGp1LmNvbSIsImlzX2FjdGl2ZSI6dHJ1ZSwib3JpZ19pYXQiOjE2Nzg3MDIzOTMsInJvbGUiOiJ1c2VyICJ9.h8x9IYXiw3ewCp-DQEnp4FU63o9Ceog6wJIA-XzDBgg"}
1. Add better validation of request inputs.
2. Return "client-friendly" standard and error responses.
3. Add more unit tests.
4. Add more API tests.
5. Explore more edge cases.
6. I have implemented RBAC using JWT-based middleware which works fine when the business use-cases are limited, but would be a pain when the scope of the system increases. So, implementing RBAC using Casbin would be better for the long run.
7. Decrease the number of files in the serializer
directory.
8. Improve route names.
9. Add contextual logging.
10. Add hashed password to DB.
- For the sake of focussing entirely on the problem statement, I have elided certain details. For example, there's no
price
attribute (among many other attributes) in theitems
schema. - I have used the DDD (Domain-Driven Design) approach to build the project, as this helps with testing each layer while only depending on the layer underneath it.
- Directory Structure:
internal/api
: The HTTP API handlers exist here.internal/domain
: The entities that reflect the nature of the business exist here.internal/middleware
: All the HTTP middlewares exist here. (only one exists now)internal/migrations
: Contains the SQL migration script and the code to run the migrations on Postgres.internal/serializer
: All the Request and Response types exist here.internal/repository
: Contains code that communicates with the database. It is used by theservice
layer.internal/service
: Contains the code that communicates with the repository.pkg/database
: Contains the database-specific code.cmd/cli
: Contains the code to execute the code.config
: Contains code to load configurations from a YAML file.