Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/create #5

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions app/controllers/api/v1/merchants/coupons_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class Api::V1::Merchants::CouponsController < ApplicationController

def show
coupon = Coupon.find(params[:id])
render json: CouponSerializer.new(coupon)
end

def index
merchant = Merchant.find(params[:merchant_id])
coupons = merchant.coupons
render json: CouponSerializer.new(coupons)
end

def create
merchant = Merchant.find(params[:merchant_id])
coupon = merchant.coupons.new(coupon_params)
if coupon.save
render json: CouponSerializer.new(coupon), status: :created
else
render json: { errors: coupon.errors.full_messages }, status: :unprocessable_entity
end
end

private

def coupon_params
params.require(:coupon).permit(:name, :code, :discount_value, :active)
end
end
7 changes: 7 additions & 0 deletions app/models/coupon.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Coupon < ApplicationRecord
belongs_to :merchant
has_many :invoices
validates :code, uniqueness: { scope: :merchant_id, message: "has already been taken" }
validates :name, :code, presence: true
validates :active, inclusion: { in: [true, false] }
end
1 change: 1 addition & 0 deletions app/models/invoice.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class Invoice < ApplicationRecord
belongs_to :customer
belongs_to :merchant
belongs_to :coupon, optional: true
has_many :invoice_items, dependent: :destroy
has_many :transactions, dependent: :destroy

Expand Down
1 change: 1 addition & 0 deletions app/models/merchant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ class Merchant < ApplicationRecord
has_many :items, dependent: :destroy
has_many :invoices, dependent: :destroy
has_many :customers, through: :invoices
has_many :coupons
# has_many :invoice_items, through: :invoices
# has_many :transactions, through: :invoices

Expand Down
4 changes: 4 additions & 0 deletions app/serializers/coupon_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class CouponSerializer
include JSONAPI::Serializer
attributes :id, :name, :code, :discount_value, :active, :merchant_id
end
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
resources :items, only: :index, controller: "merchants/items"
resources :customers, only: :index, controller: "merchants/customers"
resources :invoices, only: :index, controller: "merchants/invoices"
resources :coupons, only: [:index, :show, :create], controller: "merchants/coupons"
end
end
end
Expand Down
14 changes: 14 additions & 0 deletions db/migrate/20240919204137_create_coupons.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class CreateCoupons < ActiveRecord::Migration[7.1]
def change
create_table :coupons do |t|
t.string :name
t.string :code
t.decimal :discount_value
t.boolean :active
t.references :merchant, null: false, foreign_key: true

t.timestamps
end
add_index :coupons, :code, unique: true
end
end
5 changes: 5 additions & 0 deletions db/migrate/20240919204828_add_coupon_to_invoices.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddCouponToInvoices < ActiveRecord::Migration[7.1]
def change
add_reference :invoices, :coupon, null: true, foreign_key: true
end
end
78 changes: 77 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions spec/factories/coupons.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FactoryBot.define do
factory :coupon do
name { "MyString" }
code { "MyString" }
discount_value { "9.99" }
active { false }
merchant
end
end
24 changes: 24 additions & 0 deletions spec/models/coupon_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require 'rails_helper'

require 'rails_helper'

RSpec.describe Coupon, type: :model do
before do
@merchant = Merchant.create(name: "Test Merchant")
end

it { should belong_to(:merchant) }
it { should have_many(:invoices) }

it { should validate_presence_of(:name) }
it { should validate_presence_of(:code) }
it { should validate_inclusion_of(:active).in_array([true, false]) }

it 'validates uniqueness of code scoped to merchant_id' do
Coupon.create!(name: "Existing Coupon", code: "UNIQUECODE", active: true, merchant: @merchant)
new_coupon = Coupon.new(name: "New Coupon", code: "UNIQUECODE", active: true, merchant: @merchant)

expect(new_coupon).not_to be_valid
expect(new_coupon.errors[:code]).to include("has already been taken")
end
end
3 changes: 3 additions & 0 deletions spec/models/invoice_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
RSpec.describe Invoice do
it { should belong_to :merchant }
it { should belong_to :customer }
it { should belong_to(:coupon).optional}
it { should have_many(:invoice_items).dependent(:destroy) }
it { should have_many(:transactions).dependent(:destroy) }
it { should validate_inclusion_of(:status).in_array(%w(shipped packaged returned)) }
end
1 change: 1 addition & 0 deletions spec/models/merchant_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
it { should have_many :items }
it { should have_many :invoices }
it { should have_many(:customers).through(:invoices) }
it { should have_many(:coupons)}
end

describe "class methods" do
Expand Down
101 changes: 101 additions & 0 deletions spec/requests/api/v1/merchant/coupons_request_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
require 'rails_helper'

RSpec.describe "Coupons", type: :request do
describe "GET /show" do
it 'return one coupon by id' do
merchant = Merchant.create!(id: 1, name: "Sample Merchant")
coupon = Coupon.create!(name: "Buy One Get One 50", code: "BOGO50", discount_value: 50, active: true, merchant_id: merchant.id)

get "/api/v1/merchants/#{merchant.id}/coupons/#{coupon.id}"

expect(response).to have_http_status(200)
expect(response).to be_successful

json_response = JSON.parse(response.body)

expect(json_response['data']['id']).to eq(coupon.id.to_s)
expect(json_response['data']['type']).to eq('coupon')
expect(json_response['data']['attributes']['name']).to eq(coupon.name)
expect(json_response['data']['attributes']['code']).to eq(coupon.code)
expect(json_response['data']['attributes']['discount_value']).to be_a(String)
expect(json_response['data']['attributes']['active']).to eq(coupon.active?)
expect(json_response['data']['attributes']['merchant_id']).to eq(coupon.merchant_id)
end

it 'returns a 404 when a coupon does not exist' do
merchant = Merchant.create!(id: 1, name: "Sample Merchant")

get "/api/v1/merchants/#{merchant.id}/coupons/9999"

expect(response).to have_http_status(404)
json_response = JSON.parse(response.body)

expect(json_response['message']).to include("Your query could not be completed")
expect(json_response['errors']).to include("Couldn't find Coupon with 'id'=9999")
end
end

describe 'GET/index' do
it 'returns all coupons for certain merchant id' do
merchant = Merchant.create!(id: 1, name: "Sample Merchant")
Coupon.create!(name: "Buy One Get One 50", code: "BOGO50", discount_value: 50, active: true, merchant_id: merchant.id)
Coupon.create!(name: "Buy One Get One 40", code: "BOGO40", discount_value: 40, active: true, merchant_id: merchant.id)
Coupon.create!(name: "Buy One Get One 30", code: "BOGO30", discount_value: 30, active: false, merchant_id: merchant.id)
Coupon.create!(name: "Buy One Get One 20", code: "BOGO20", discount_value: 20, active: false, merchant_id: merchant.id)

get "/api/v1/merchants/#{merchant.id}/coupons"

expect(response).to be_successful

json_response = JSON.parse(response.body, symbolize_names: true)[:data]
expect(json_response.size).to eq(4)

json_response.each do |coupon|
expect(coupon[:id]).to be_a(String)
expect(coupon[:type]).to eq('coupon')
expect(coupon[:attributes]).to have_key(:id)
expect(coupon[:attributes][:id]).to be_an(Integer)
expect(coupon[:attributes][:name]).to be_a(String)
expect(coupon[:attributes][:code]).to be_a(String)
expect(coupon[:attributes][:discount_value]).to be_a(String)
expect([true, false]).to include(coupon[:attributes][:active])
expect(coupon[:attributes][:merchant_id]).to be_an(Integer)
end
end

it 'returns an error is there is no merchant with that id' do
merchant = Merchant.create!(id: 1, name: "Sample Merchant")
Coupon.create!(name: "Buy One Get One 50", code: "BOGO50", discount_value: 50, active: true, merchant_id: merchant.id)
Coupon.create!(name: "Buy One Get One 40", code: "BOGO40", discount_value: 40, active: true, merchant_id: merchant.id)
Coupon.create!(name: "Buy One Get One 30", code: "BOGO30", discount_value: 30, active: false, merchant_id: merchant.id)
Coupon.create!(name: "Buy One Get One 20", code: "BOGO20", discount_value: 20, active: false, merchant_id: merchant.id)

get "/api/v1/merchants/9999/coupons"

expect(response).to have_http_status(404)

json_response = JSON.parse(response.body)

expect(json_response['message']).to include("Your query could not be completed")
expect(json_response['errors']).to include("Couldn't find Merchant with 'id'=9999")
end
end

describe 'POST' do
it 'creates a new coupon' do
merchant = Merchant.create!(name: "Sample Merchant")
coupon_params = { name: "Buy One Get One 50", code: "BOGO50", discount_value: 50, active: true }

post "/api/v1/merchants/#{merchant.id}/coupons", params: { coupon: coupon_params }

expect(response).to have_http_status(201)

created_coupon = Coupon.last
expect(created_coupon.name).to eq("Buy One Get One 50")
expect(created_coupon.code).to eq("BOGO50")
expect(created_coupon.discount_value).to eq(50)
expect(created_coupon.active).to be_truthy
expect(created_coupon.merchant_id).to eq(merchant.id)
end
end
end