This repo project is an API and database for a simple Point of Sale (POS) system. A second repo exists for a corresponding web client
Below follows background information, you can also just skip ahead to the API Docs if you like.
This is my second project as part of the General Assembly Web Development Immersive program.
We were given the freedom to choose what to develop as long as it met the following requirements/specifications:
Be deployed online, where the rest of the world can access it.
- Deploy client application on GH pages.
- Deploy server application on Heroku.
- Signup with email, password, and password confirmation.
- Login with email and password.
- Logout when logged in.
- Change password with current and new password.
- Give feedback to the user after each action's success or failure.
- Use a front-end Javascript app to communicate with your API (both read and write) and render data that it receives in the browser.
- Have semantically clean HTML and CSS
- User must be able to create a new resource
- User must be able to update a resource
- User must be able to delete a resource
- User must be able to view a single or multiple resource(s)
- Give feedback to the user after each action's success or failure.
- Create at least 4 RESTful routes for handling GET, POST, PUT/PATCH, and DELETE requests.
- Any actions which change data must be authenticated and the data must be "owned" by the user performing the change.
- Utilize an ORM to create a database table structure and interact with data
- Have at least 1 resource that has a relationship to User
I chose to develop a simple POS based on my interest and experience with order processes and systems.
Earlier in my career I worked with the SAP order to cash product which gave me a lot of exposure to that database and the associated processes.
More recently I've configured and worked with some SaaS POS systems such as Shopify, Square, and Vend. I also have an interest in the Toast product.
I started by putting my initial thoughts about a POS system into a mind map. This helped me organize my various thoughts and from that I was able to start to conceptualize what my ERD might look like.
I then created an ERD. The ERD builds upon itself for what I thought would be five clear stages of product development. I did not expect to be able to complete all five stages during the project week but having the additional phases helped me understand what logically followed.
Currently the app API allows the following functionality
Users
- Sign-up (create a user)
- Sign-in
- Change password
- Sign-out
- List all users
- Display an individual user
Orders
- Create an order
- Update an order
- Delete an order
- List all orders
- Display an individual order
The app and api are currently deployed here:
api https://square-toast.herokuapp.com/ app https://johnsnedden.github.io/square-toast-client/
- I'm not entirely happy with the lack of company component in this initial version. With the current setup multiple users can not share orders, like they might if they were working at the same food service establishment. With the requirements of this project focussig on user owned data I did not attempt to tackle this company owned data aspect during project week.
There is a lot of scope for building out this product. To begin with I'd like to add the following resources and incorporate them into the database and API:
- Company - Enables users to belong to one or more companies, and orders to belong to a single company.
- Company authorization - Allow users to view/edit all orders for all companies they are connected to.
- Order Items - Allow each order to have multiple items.
- Products - Predefined products which can entered into orders.
- Customer - Allow orders to be associated with a customer.
Beyond the above items I have many more concepts on the initial mind map which can be assessed for product market fit and feasibility based on user feedback.
Scripts are included in scripts
to test built-in actions.
Verb | URI Pattern | Controller#Action |
---|---|---|
POST | /sign-up |
users#signup |
POST | /sign-in |
users#signin |
PATCH | /change-password/:id |
users#changepw |
DELETE | /sign-out/:id |
users#signout |
Request:
curl http://localhost:4741/sign-up \
--include \
--request POST \
--header "Content-Type: application/json" \
--data '{
"credentials": {
"email": "'"${EMAIL}"'",
"password": "'"${PASSWORD}"'",
"password_confirmation": "'"${PASSWORD}"'"
}
}'
EMAIL=ava@bob.com PASSWORD=hannah scripts/auth/sign-up.sh
Response:
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
{
"user": {
"id": 1,
"email": "ava@bob.com"
}
}
Request:
curl http://localhost:4741/sign-in \
--include \
--request POST \
--header "Content-Type: application/json" \
--data '{
"credentials": {
"email": "'"${EMAIL}"'",
"password": "'"${PASSWORD}"'"
}
}'
EMAIL=ava@bob.com PASSWORD=hannah scripts/auth/sign-in.sh
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"user": {
"id": 1,
"email": "ava@bob.com",
"token": "BAhJIiVlZDIwZTMzMzQzODg5NTBmYjZlNjRlZDZlNzYxYzU2ZAY6BkVG--7e7f77f974edcf5e4887b56918f34cd9fe293b9f"
}
}
Request:
curl --include --request PATCH "http://localhost:4741/change-password/$ID" \
--header "Authorization: Token token=$TOKEN" \
--header "Content-Type: application/json" \
--data '{
"passwords": {
"old": "'"${OLDPW}"'",
"new": "'"${NEWPW}"'"
}
}'
ID=1 OLDPW=hannah NEWPW=elle TOKEN=BAhJIiVlZDIwZTMzMzQzODg5NTBmYjZlNjRlZDZlNzYxYzU2ZAY6BkVG--7e7f77f974edcf5e4887b56918f34cd9fe293b9f scripts/auth/change-password.sh
Response:
HTTP/1.1 204 No Content
Request:
curl http://localhost:4741/sign-out/$ID \
--include \
--request DELETE \
--header "Authorization: Token token=$TOKEN"
ID=1 TOKEN=BAhJIiVlZDIwZTMzMzQzODg5NTBmYjZlNjRlZDZlNzYxYzU2ZAY6BkVG--7e7f77f974edcf5e4887b56918f34cd9fe293b9f scripts/auth/sign-out.sh
Response:
HTTP/1.1 204 No Content
Verb | URI Pattern | Controller#Action |
---|---|---|
GET | /users |
users#index |
GET | /users/1 |
users#show |
Request:
curl http://localhost:4741/users \
--include \
--request GET \
--header "Authorization: Token token=$TOKEN"
TOKEN=BAhJIiVlZDIwZTMzMzQzODg5NTBmYjZlNjRlZDZlNzYxYzU2ZAY6BkVG--7e7f77f974edcf5e4887b56918f34cd9fe293b9f scripts/users/users.sh
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"users": [
{
"id": 2,
"email": "bob@ava.com"
},
{
"id": 1,
"email": "ava@bob.com"
}
]
}
Request:
curl --include --request GET http://localhost:4741/users/$ID \
--header "Authorization: Token token=$TOKEN"
ID=2 TOKEN=BAhJIiVlZDIwZTMzMzQzODg5NTBmYjZlNjRlZDZlNzYxYzU2ZAY6BkVG--7e7f77f974edcf5e4887b56918f34cd9fe293b9f scripts/users/user.sh
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"user": {
"id": 2,
"email": "bob@ava.com"
}
}
Verb | URI Pattern | Controller#Action |
---|---|---|
GET | /orders |
orders#index |
GET | /orders/1 |
orders#show |
POST | /orders |
orders#create |
PATCH | /orders/1 |
orders#update |
DELETE | /orders/1 |
orders#destroy |
Request:
curl http://localhost:4741/orders \
--include \
--request GET \
--header "Authorization: Token token=$TOKEN"
TOKEN=BAhJIiVlZDIwZTMzMzQzODg5NTBmYjZlNjRlZDZlNzYxYzU2ZAY6BkVG--7e7f77f974edcf5e4887b56918f34cd9fe293b9f scripts/order/orders.sh
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"orders": [
{
"id": 21,
"date": "2017-10-20",
"status": "open",
"description": "rice bowl with chicken",
"price": "10.0",
"user_id": 1
},
{
"id": 22,
"date": "2017-10-20",
"status": "open",
"description": "bacon milkshake",
"price": "6.5",
"user_id": 1
}
]
}
Request:
curl --include --request GET http://localhost:4741/orders/$ID \
--header "Authorization: Token token=$TOKEN"
ID=61 TOKEN=BAhJIiVlZDIwZTMzMzQzODg5NTBmYjZlNjRlZDZlNzYxYzU2ZAY6BkVG--7e7f77f974edcf5e4887b56918f34cd9fe293b9f scripts/orders/show.sh
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"order": {
"id": 61,
"date": "2017-10-23",
"status": "Open",
"description": "carnitas bowl + rice",
"price": "9.5",
"user_id": 1
}
}
Request:
curl http://localhost:4741/orders \
--include \
--request POST \
--header "Content-Type: application/json" \
--header "Authorization: Token token=$TOKEN" \
--data '{
"order": {
"date": "'"${DATE}"'",
"status": "'"${STATUS}"'",
"description": "'"${DESCRIPTION}"'",
"price": "'"${PRICE}"'"
}
}'
DATE="2017-10-15" STATUS="open" DESCRIPTION="wood fired pizza" PRICE="11.50" TOKEN=BAhJIiVlZDIwZTMzMzQzODg5NTBmYjZlNjRlZDZlNzYxYzU2ZAY6BkVG--7e7f77f974edcf5e4887b56918f34cd9fe293b9f scripts/order/create.sh
Response:
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
{
"order": {
"id": 26,
"date": "2017-10-15",
"status": "open",
"description": "wood fired pizza",
"price": "11.5",
"user_id": 1,
}
}
Request:
curl --include --request PATCH "http://localhost:4741/orders/$ID" \
--header "Authorization: Token token=$TOKEN" \
--header "Content-Type: application/json" \
--data '{
"order": {
"date": "'"${DATE}"'",
"status": "'"${STATUS}"'",
"description": "'"${DESCRIPTION}"'",
"price": "'"${PRICE}"'"
}
}'
ID=64 DATE="2017-10-24" STATUS="Open" DESCRIPTION="All you can eat pancakes" PRICE="19.00" TOKEN=BAhJIiVlZDIwZTMzMzQzODg5NTBmYjZlNjRlZDZlNzYxYzU2ZAY6BkVG--7e7f77f974edcf5e4887b56918f34cd9fe293b9f scripts/order/update.sh
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"order": {
"id": 64,
"date": "2017-10-24",
"status": "Open",
"description": "All you can eat pancakes",
"price": "19.0",
"user_id": 1,
}
}
Request:
curl http://localhost:4741/orders/$ID \
--include \
--request DELETE \
--header "Authorization: Token token=$TOKEN"
ID=1 TOKEN=BAhJIiVlZDIwZTMzMzQzODg5NTBmYjZlNjRlZDZlNzYxYzU2ZAY6BkVG--7e7f77f974edcf5e4887b56918f34cd9fe293b9f scripts/order/destroy.sh
Response:
HTTP/1.1 204 No Content