Skip to content

buzuloiu/shopify-2019

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Shopify Summer 2019 Application

CI Status codecov

Welcome to my basic online store!

Overview

  • This project is a REST API built with Rails 5 and Ruby 2.6.0.
  • It uses a Postgres database.
  • It's a basic marketplace with products and carts
  • It's secured with JWT and throttles requests by token.
  • It's hosted at: https://shopify-intern-api.herokuapp.com/api/v1/

Docs and Testing

The API reference for this app is located here: https://shopify-intern-api.herokuapp.com/api/v1/docs

Testing the Endpoints

All the endpoints have specs written for them with RSpec (and some 🎩). see /spec/acceptance. The docs for the API are generated by running the specs. If any specs don't pass, the documentation for that endpoint will not be generated. This way a broken feature won't be put into the API reference.

Unit Testing

Unit tests are written using the built in rails testing, FactoryBot, and Faker

Getting Started

Making requests

The API uses HTTP requests. You can use a CLI tool like curl, but I found it easier to use something like Postman

You should make your requests to the /api/v1/ endpoints.

Let's try getting all of the products sold by the store with a GET request with curl:

curl https://shopify-intern-api.herokuapp.com/api/v1/products

you should get a response like this:

{"error":"Not Authorized"}

This is because you need to log in and get a token with which to make requests.

Creating an account

To create a user with curl, POST to the /users endpoint like this:

curl -H "Content-Type: application/json" -X POST -d '{"user":{"name":"<your_name>", "email":"<your_email>","password":"<your_passsword>"}}' http://shopify-intern-api.herokuapp.com/users

and the API should repond with:

{"name": "<your_name>", "email": "your_email"}

and now you're ready to start making authorized requests!

Making authorized requests

To make an authorized requests you have add your auth token as Authorization in the header of your request. To get your token, you can POST to the /authenticate endpoint with like this:

curl -H "Content-Type: application/json" -X POST -d '{"email":"<your_email>","password":"<your_passsword>"}' http://shopify-intern-api.herokuapp.com/authenticate

the api will respond with:

{"auth_token":"<your_token_here>"}

To use the token with curl, make a request with your auth token as Authorization in the header of your request like this:

curl -H "Authorization: <your_token_here>" http://shopify-intern-api.herokuapp.com/api/v1/products

and the API should respond to your request by showing you all the products in the store.

Demo

  • can be used to fetch products either one at a time or all at once.
  • Every product should have a title, price, and inventory_count.

request:

curl -H "Content-Type: application/json" -X POST -d '{"user":{"name":"test", "email":"[email protected]","password":"password"}}' http://shopify-intern-api.herokuapp.com/users
{"name":"test","email":"[email protected]"}

request:

curl -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"password"}' http://shopify-intern-api.herokuapp.com/authenticate
{"auth_token":"eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjozLCJleHAiOjE1NDgxMzczOTF9.FZ4XDEA9XrldO4KHQY19s-D4S-AWcoH_s9JhnW0iFPA"}

request:

curl -H "Authorization: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjozLCJleHAiOjE1NDgxMzczOTF9.FZ4XDEA9XrldO4KHQY19s-D4S-AWcoH_s9JhnW0iFPA" http://shopify-intern-api.herokuapp.com/api/v1/products
[
    {
        "id": 1,
        "title": "Intelligent Wool Coat",
        "inventory_count": 74,
        "price_cents": 3468,
        "price_currency": "USD"
    },
    {
        "id": 2,
        "title": "Heavy Duty Leather Pants",
        "inventory_count": 76,
        "price_cents": 7502,
        "price_currency": "USD"
    },
    {
        "id": 3,
        "title": "Lightweight Concrete Bottle",
        "inventory_count": 31,
        "price_cents": 2025,
        "price_currency": "USD"
    },
    {
        "id": 4,
        "title": "Synergistic Steel Bench",
        "inventory_count": 0,
        "price_cents": 8861,
        "price_currency": "USD"
    }
]

one product at a time: request:

curl -H "Authorization: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjozLCJleHAiOjE1NDgxMzczOTF9.FZ4XDEA9XrldO4KHQY19s-D4S-AWcoH_s9JhnW0iFPA" http://shopify-intern-api.herokuapp.com/api/v1/products/2
{
    "id": 2,
    "title": "Heavy Duty Leather Pants",
    "inventory_count": 76,
    "price_cents": 7502,
    "price_currency": "USD"
}
  • Querying for all products should support passing an argument to only return products with available inventory.

request:

curl -H "Authorization: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjozLCJleHAiOjE1NDgxMzczOTF9.FZ4XDEA9XrldO4KHQY19s-D4S-AWcoH_s9JhnW0iFPA" http://shopify-intern-api.herokuapp.com/api/v1/products?qty_available=1
[
    {
        "id": 1,
        "title": "Intelligent Wool Coat",
        "inventory_count": 74,
        "price_cents": 3468,
        "price_currency": "USD"
    },
    {
        "id": 2,
        "title": "Heavy Duty Leather Pants",
        "inventory_count": 76,
        "price_cents": 7502,
        "price_currency": "USD"
    },
    {
        "id": 3,
        "title": "Lightweight Concrete Bottle",
        "inventory_count": 31,
        "price_cents": 2025,
        "price_currency": "USD"
    }
]
  • Products should be able to be "purchased" which should reduce the inventory by 1.

request:

curl -H "Authorization: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjozLCJleHAiOjE1NDgxMzczOTF9.FZ4XDEA9XrldO4KHQY19s-D4S-AWcoH_s9JhnW0iFPA" http://shopify-intern-api.herokuapp.com/api/v1/products/2
{
    "id": 2,
    "title": "Heavy Duty Leather Pants",
    "inventory_count": 76,
    "price_cents": 7502,
    "price_currency": "USD"
}

request:

curl -H "Authorization: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjozLCJleHAiOjE1NDgxMzczOTF9.FZ4XDEA9XrldO4KHQY19s-D4S-AWcoH_s9JhnW0iFPA" -X PUT http://shopify-intern-api.herokuapp.com/api/v1/products/2/purchase
{
    "id": 2,
    "title": "Heavy Duty Leather Pants",
    "inventory_count": 75,
    "price_cents": 7502,
    "price_currency": "USD"
}
  • Products with no inventory cannot be purchased.

request:

curl -H "Authorization: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjozLCJleHAiOjE1NDgxMzczOTF9.FZ4XDEA9XrldO4KHQY19s-D4S-AWcoH_s9JhnW0iFPA" http://shopify-intern-api.herokuapp.com/api/v1/products/4
{
    "id": 4,
    "title": "Synergistic Steel Bench",
    "inventory_count": 0,
    "price_cents": 8861,
    "price_currency": "USD"
}

request:

curl -H "Authorization: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjozLCJleHAiOjE1NDgxMzczOTF9.FZ4XDEA9XrldO4KHQY19s-D4S-AWcoH_s9JhnW0iFPA" -X PUT http://shopify-intern-api.herokuapp.com/api/v1/products/4/purchase
{
    "inventory_count": [
        "must be greater than -1"
    ]
}

Fit these product purchases into the context of a simple shopping cart.

  1. creating a cart

request:

curl -H "Authorization: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJleHAiOjE1NDgxMzk2Njd9.Y4Fh2GPz0P40W31_tT6R4o9AtD_e9zJ0GCb_p50DAaE" -X POST http://shopify-intern-api.herokuapp.com/api/v1/carts
{
    "id": 2,
    "total_cents": 0,
    "total_currency": "USD",
    "completed_at": null,
    "line_items": []
}
  1. adding products to the cart, The cart should contain a list of all included products, a total dollar amount (the total value of all products), and product inventory shouldn't reduce until after a cart has been completed.

request:

curl -H "Content-Type: application/json" -H "Authorization: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJleHAiOjE1NDgxMzk2Njd9.Y4Fh2GPz0P40W31_tT6R4o9AtD_e9zJ0GCb_p50DAaE" -X PUT -d '{"product_id":2,"quantity":5}' http://shopify-intern-api.herokuapp.com/api/v1/carts/2/add/
{
    "id": 2,
    "total_cents": 37510,
    "total_currency": "USD",
    "completed_at": null,
    "line_items": [
        {
            "product_id": 2,
            "quantity": 5,
            "unit_price_cents": 7502,
            "unit_price_currency": "USD",
            "total_price_cents": 37510,
            "total_price_currency": "USD"
        }
    ]
}
  • "completing" the cart

request: (app checks if all line items are available before attempting to purchase them )

curl -H "Authorization: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJleHAiOjE1NDgxMzk2Njd9.Y4Fh2GPz0P40W31_tT6R4o9AtD_e9zJ0GCb_p50DAaE" -X PUT http://shopify-intern-api.herokuapp.com/api/v1/carts/2/complete
{
    "id": 1,
    "total_cents": 37510,
    "total_currency": "USD",
    "completed_at": "2019-01-21T06:59:07.087Z",
    "line_items": [
        {
            "product_id": 2,
            "quantity": 5,
            "unit_price_cents": 7502,
            "unit_price_currency": "USD",
            "total_price_cents": 37510,
            "total_price_currency": "USD"
        }
    ]
}

Design

Products

Products are the most basic model in the app, with just a title, inventory_count and price_cents.

Line Items

Line items belong to products. Line items go in shopping carts, and act as a nice way to build a list of items for a cart while keeping track of how many are in the cart.

Carts

Carts have many line_items. when attempting to complete a cart, the cart will first check that each of the line items are available before "purchasing the products"

Securing The API

JWT

JSON web tokens are used to authenticate users. All endpoints require an auth token in their header to process the request except for the docs, the user creation endpoint, and the user authenication endpoint.

In a real production environment, users would be validated by email confirmation, so using JWT the way it's implemented here would help secure the API against farming and other similar abuse.

Throttling

The rack-defense gem allows for requests to be throttled to prevent DDOS attacks. in this case, tokens can only make 50 requests per second to any endpoint that starts with /api

Ideally, we would also be able to keep track of which user creations were successful, and only allow a fixed number of successful account creations per IP address to further prevent DDOS.

About

Shopify API challenge for summer 2019 interns

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages