From f8204112715c17885dfd13b80e7347e1ba0c39c0 Mon Sep 17 00:00:00 2001 From: Dewi-Anggraini Date: Mon, 13 Jan 2025 19:54:01 +0700 Subject: [PATCH 1/5] HER-66 creating address controller --- .../api/v1/addresses_controller.rb | 42 +++++++ app/controllers/api/v1/orders_controller.rb | 21 +++- app/models/address.rb | 2 + app/models/order.rb | 1 + config/routes.rb | 4 +- ...110130617_add_save_to_user_to_addresses.rb | 5 + db/schema.rb | 3 +- spec/factories/addresses.rb | 1 + spec/requests/addresses_spec.rb | 103 ++++++++++++++++++ 9 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 app/controllers/api/v1/addresses_controller.rb create mode 100644 db/migrate/20250110130617_add_save_to_user_to_addresses.rb create mode 100644 spec/requests/addresses_spec.rb diff --git a/app/controllers/api/v1/addresses_controller.rb b/app/controllers/api/v1/addresses_controller.rb new file mode 100644 index 00000000..5c0aaa46 --- /dev/null +++ b/app/controllers/api/v1/addresses_controller.rb @@ -0,0 +1,42 @@ +class Api::V1::AddressesController < ApplicationController + before_action :set_addressable + + def create + @address = @addressable.addresses.build(address_params) + if @address.save + render json: @address, status: :created + else + render json: @address.errors, status: :unprocessable_entity + end + end + + def update + @address = @addressable.addresses.find(params[:id]) + if @address.update(address_params) + render json: @address, status: :ok + else + render json: @address.errors, status: :unprocessable_entity + end + end + + def destroy + @address = @addressable.addresses.find(params[:id]) + @address.destroy + head :no_content + end + + private + + def set_addressable + @addressable = if params[:user_id] + User.find(params[:user_id]) + elsif params[:organization_id] + Organization.find(params[:organization_id]) + end + end + + def address_params + params.require(:address).permit(:street_address, :city, :state, :postal_code, :save_to_user) + end + +end diff --git a/app/controllers/api/v1/orders_controller.rb b/app/controllers/api/v1/orders_controller.rb index 842add1e..618f86e6 100644 --- a/app/controllers/api/v1/orders_controller.rb +++ b/app/controllers/api/v1/orders_controller.rb @@ -8,9 +8,10 @@ def index # POST /api/v1/orders def create - @order.user = current_user # Automatically associate user - + @order = Order.new(order_params) + @order.user = current_user if @order.save + associate_address_with_user(@order) render json: @order, status: :created else render json: { errors: @order.errors.full_messages }, status: :unprocessable_entity @@ -25,6 +26,7 @@ def show # PATCH/PUT /api/v1/orders/:id def update if @order.update(order_params) + associate_address_with_user(@order) render json: @order, status: :ok else render json: { errors: @order.errors.full_messages }, status: :unprocessable_entity @@ -40,7 +42,22 @@ def destroy private + def set_order + @order = Order.find(params[:id]) + end + def order_params params.require(:order).permit(:phone, :address_id, :school_year, :comments, :product_id, :product_type) end + + def associate_address_with_user(order) + if order.address && order.user + # Add a condition to check if the address should be saved to user + if order.address.save_to_user + unless order.user.addresses.exists?(order.address.id) + order.user.addresses << order.address + end + end + end + end end diff --git a/app/models/address.rb b/app/models/address.rb index 49940172..034d3585 100644 --- a/app/models/address.rb +++ b/app/models/address.rb @@ -2,6 +2,8 @@ class Address < ApplicationRecord belongs_to :addressable, polymorphic: true has_many :orders + attribute :save_to_user, :boolean, default: false + validates :street_address, :city, :state, :postal_code, :addressable, presence: true validates :postal_code, format: { with: /\A\d{5}(-\d{4})?\z/, message: "must be a valid postal code" } end diff --git a/app/models/order.rb b/app/models/order.rb index 55fe91fa..4f3fc094 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -3,6 +3,7 @@ class Order < ApplicationRecord belongs_to :product, polymorphic: true belongs_to :address + accepts_nested_attributes_for :address, allow_destroy: true before_validation :normalize_phone_number # Validates that phone number is in the right 10 digit format diff --git a/config/routes.rb b/config/routes.rb index 30cefc1e..ec75a572 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -22,7 +22,9 @@ post "kit_items_only", to: "kit_items#create_kit_items_only" patch "kit_items_only/:id", to: "kit_items#update_kit_items_only" get "profile", to: "users#profile" - resources :users + resources :users do + resources :addresses, only: [ :create, :update, :destroy ] + end resources :donations resources :contacts resources :events diff --git a/db/migrate/20250110130617_add_save_to_user_to_addresses.rb b/db/migrate/20250110130617_add_save_to_user_to_addresses.rb new file mode 100644 index 00000000..8a85266c --- /dev/null +++ b/db/migrate/20250110130617_add_save_to_user_to_addresses.rb @@ -0,0 +1,5 @@ +class AddSaveToUserToAddresses < ActiveRecord::Migration[7.2] + def change + add_column :addresses, :save_to_user, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index 096c330a..382e8f40 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2025_01_07_051631) do +ActiveRecord::Schema[7.2].define(version: 2025_01_10_130617) do create_table "active_storage_attachments", force: :cascade do |t| t.string "name", null: false t.string "record_type", null: false @@ -48,6 +48,7 @@ t.datetime "updated_at", null: false t.string "addressable_type", null: false t.integer "addressable_id", null: false + t.boolean "save_to_user" t.index ["addressable_type", "addressable_id"], name: "index_addresses_on_addressable" end diff --git a/spec/factories/addresses.rb b/spec/factories/addresses.rb index bc3de5c2..27579011 100644 --- a/spec/factories/addresses.rb +++ b/spec/factories/addresses.rb @@ -4,6 +4,7 @@ city { Faker::Address.city } state { Faker::Address.state_abbr } postal_code { Faker::Address.zip_code } + save_to_user { true } association :addressable, factory: :user # Default polymorphic association end end diff --git a/spec/requests/addresses_spec.rb b/spec/requests/addresses_spec.rb new file mode 100644 index 00000000..87309265 --- /dev/null +++ b/spec/requests/addresses_spec.rb @@ -0,0 +1,103 @@ +require 'rails_helper' + +RSpec.describe "Addresses", type: :request do + let(:regular_user) { create(:user, :regular_user) } + let(:admin_user) { create(:user, :admin) } + let(:address) { create(:address, addressable: regular_user) } + + describe "POST /create" do + let(:address) { create(:address, addressable: regular_user) } + + context "when the user is regular user" do + it "creates new address" do + sign_in regular_user + address_params = { + address: { + street_address: "123 St", + city: "City1", + state: "State1", + postal_code: "12345", + save_to_user: true + } + } + expect { + post "/api/v1/users/#{regular_user.id}/addresses", params: address_params, headers: { 'Authorization': "Bearer #{@auth_token}" } + }.to change(Address, :count).by(1) + expect(response).to have_http_status(:created) + end + end + + context "when the user is admin user" do + it "can create new address for user" do + sign_in admin_user + address_params = { + address: { + street_address: "123 St", + city: "City1", + state: "State1", + postal_code: "12345", + save_to_user: true + } + } + + expect { + post "/api/v1/users/#{admin_user.id}/addresses", params: address_params, headers: { 'Authorization': "Bearer #{@auth_token}" } + }.to change(Address, :count).by(1) + expect(response).to have_http_status(:created) + end + end + end + + describe "PUT /update" do + let(:address) { create(:address, addressable: regular_user) } + let(:updated_address_params) { + { address: { + street_address: "456 St", + city: "City1", + state: "State1", + postal_code: "12345", + save_to_user: true + } } + } + + context "when the user is regular user" do + it "can update their own address" do + sign_in regular_user + put "/api/v1/users/#{regular_user.id}/addresses/#{address.id}", params: updated_address_params, headers: { 'Authorization': "Bearer #{@auth_token}" } + expect(response).to have_http_status(:ok) + end + end + + context "when the user is admin user" do + it "can update any address" do + sign_in admin_user + put "/api/v1/users/#{regular_user.id}/addresses/#{address.id}", params: updated_address_params, headers: { 'Authorization': "Bearer #{@auth_token}" } + expect(response).to have_http_status(:ok) + end + end + end + + describe "DELETE /destroy" do + let!(:address) { create(:address, addressable: regular_user) } + + context "when the user is regular user" do + it "can delete their own address" do + sign_in regular_user + expect { + delete "/api/v1/users/#{regular_user.id}/addresses/#{address.id}", headers: { 'Authorization': "Bearer #{@auth_token}" } + }.to change(Address, :count).by(-1) + expect(response).to have_http_status(:no_content) + end + end + + context "when the user is admin user" do + it "can delete any address" do + sign_in admin_user + expect { + delete "/api/v1/users/#{regular_user.id}/addresses/#{address.id}", headers: { 'Authorization': "Bearer #{@auth_token}" } + }.to change(Address, :count).by(-1) + expect(response).to have_http_status(:no_content) + end + end + end +end From 96eca8be446945c0308b428662a7b0f3cfbed5e5 Mon Sep 17 00:00:00 2001 From: Dewi-Anggraini Date: Mon, 13 Jan 2025 21:16:22 +0700 Subject: [PATCH 2/5] fixing linter and added some changes --- app/controllers/api/v1/addresses_controller.rb | 13 ++++++------- app/controllers/api/v1/orders_controller.rb | 6 +++--- spec/requests/addresses_spec.rb | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/controllers/api/v1/addresses_controller.rb b/app/controllers/api/v1/addresses_controller.rb index 5c0aaa46..bc058d24 100644 --- a/app/controllers/api/v1/addresses_controller.rb +++ b/app/controllers/api/v1/addresses_controller.rb @@ -12,11 +12,11 @@ def create def update @address = @addressable.addresses.find(params[:id]) - if @address.update(address_params) - render json: @address, status: :ok - else - render json: @address.errors, status: :unprocessable_entity - end + if @address.update(address_params) + render json: @address, status: :ok + else + render json: @address.errors, status: :unprocessable_entity + end end def destroy @@ -32,11 +32,10 @@ def set_addressable User.find(params[:user_id]) elsif params[:organization_id] Organization.find(params[:organization_id]) - end + end end def address_params params.require(:address).permit(:street_address, :city, :state, :postal_code, :save_to_user) end - end diff --git a/app/controllers/api/v1/orders_controller.rb b/app/controllers/api/v1/orders_controller.rb index 618f86e6..6359c4b5 100644 --- a/app/controllers/api/v1/orders_controller.rb +++ b/app/controllers/api/v1/orders_controller.rb @@ -9,7 +9,7 @@ def index # POST /api/v1/orders def create @order = Order.new(order_params) - @order.user = current_user + @order.user = current_user if @order.save associate_address_with_user(@order) render json: @order, status: :created @@ -52,10 +52,10 @@ def order_params def associate_address_with_user(order) if order.address && order.user - # Add a condition to check if the address should be saved to user + # Add a condition to check if the address should be saved to user if order.address.save_to_user unless order.user.addresses.exists?(order.address.id) - order.user.addresses << order.address + order.user.addresses << order.address end end end diff --git a/spec/requests/addresses_spec.rb b/spec/requests/addresses_spec.rb index 87309265..e0087958 100644 --- a/spec/requests/addresses_spec.rb +++ b/spec/requests/addresses_spec.rb @@ -2,7 +2,7 @@ RSpec.describe "Addresses", type: :request do let(:regular_user) { create(:user, :regular_user) } - let(:admin_user) { create(:user, :admin) } + let(:admin_user) { create(:user, :admin_user) } let(:address) { create(:address, addressable: regular_user) } describe "POST /create" do From 812522761f430bb0f65fee945ea6d09611d1048c Mon Sep 17 00:00:00 2001 From: Dewi-Anggraini Date: Mon, 13 Jan 2025 21:34:28 +0700 Subject: [PATCH 3/5] resolved linter issue --- app/controllers/api/v1/addresses_controller.rb | 8 ++++---- app/controllers/api/v1/orders_controller.rb | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/controllers/api/v1/addresses_controller.rb b/app/controllers/api/v1/addresses_controller.rb index bc058d24..c972f4f6 100644 --- a/app/controllers/api/v1/addresses_controller.rb +++ b/app/controllers/api/v1/addresses_controller.rb @@ -29,10 +29,10 @@ def destroy def set_addressable @addressable = if params[:user_id] - User.find(params[:user_id]) - elsif params[:organization_id] - Organization.find(params[:organization_id]) - end + User.find(params[:user_id]) + elsif params[:organization_id] + Organization.find(params[:organization_id]) + end end def address_params diff --git a/app/controllers/api/v1/orders_controller.rb b/app/controllers/api/v1/orders_controller.rb index 6359c4b5..562fd47b 100644 --- a/app/controllers/api/v1/orders_controller.rb +++ b/app/controllers/api/v1/orders_controller.rb @@ -52,7 +52,7 @@ def order_params def associate_address_with_user(order) if order.address && order.user - # Add a condition to check if the address should be saved to user + # Add a condition to check if the address should be saved to user if order.address.save_to_user unless order.user.addresses.exists?(order.address.id) order.user.addresses << order.address From ff5f75fd9162a86c4235dbe7e592dd8e586cc91c Mon Sep 17 00:00:00 2001 From: Dewi-Anggraini Date: Tue, 14 Jan 2025 09:17:49 +0700 Subject: [PATCH 4/5] added some changes for addresses_spec --- app/controllers/api/v1/orders_controller.rb | 2 +- spec/requests/addresses_spec.rb | 82 ++++++++++++++++++++- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/v1/orders_controller.rb b/app/controllers/api/v1/orders_controller.rb index 562fd47b..5cf5ca51 100644 --- a/app/controllers/api/v1/orders_controller.rb +++ b/app/controllers/api/v1/orders_controller.rb @@ -52,7 +52,7 @@ def order_params def associate_address_with_user(order) if order.address && order.user - # Add a condition to check if the address should be saved to user + # Add a condition to check if the address should be saved to user if order.address.save_to_user unless order.user.addresses.exists?(order.address.id) order.user.addresses << order.address diff --git a/spec/requests/addresses_spec.rb b/spec/requests/addresses_spec.rb index e0087958..3c366f4c 100644 --- a/spec/requests/addresses_spec.rb +++ b/spec/requests/addresses_spec.rb @@ -1,8 +1,10 @@ require 'rails_helper' RSpec.describe "Addresses", type: :request do - let(:regular_user) { create(:user, :regular_user) } let(:admin_user) { create(:user, :admin_user) } + let(:regular_user) { create(:user, :regular_user) } + let(:teacher_user) { create(:user, :teacher_user) } + let(:speaker_user) { create(:user, :speaker_user) } let(:address) { create(:address, addressable: regular_user) } describe "POST /create" do @@ -46,6 +48,46 @@ expect(response).to have_http_status(:created) end end + + context "when the user is teacher_user" do + it "can create new address for teacher user" do + sign_in teacher_user + address_params = { + address: { + street_address: "123 St", + city: "City1", + state: "State1", + postal_code: "12345", + save_to_user: true + } + } + + expect { + post "/api/v1/users/#{teacher_user.id}/addresses", params: address_params, headers: { 'Authorization': "Bearer #{@auth_token}" } + }.to change(Address, :count).by(1) + expect(response).to have_http_status(:created) + end + end + + context "when the user is speaker user" do + it "can create new address for speaker user" do + sign_in speaker_user + address_params = { + address: { + street_address: "123 St", + city: "City1", + state: "State1", + postal_code: "12345", + save_to_user: true + } + } + + expect { + post "/api/v1/users/#{speaker_user.id}/addresses", params: address_params, headers: { 'Authorization': "Bearer #{@auth_token}" } + }.to change(Address, :count).by(1) + expect(response).to have_http_status(:created) + end + end end describe "PUT /update" do @@ -69,12 +111,28 @@ end context "when the user is admin user" do - it "can update any address" do + it "can update an address" do sign_in admin_user put "/api/v1/users/#{regular_user.id}/addresses/#{address.id}", params: updated_address_params, headers: { 'Authorization': "Bearer #{@auth_token}" } expect(response).to have_http_status(:ok) end end + + context "when the user is teacher user" do + it "can update an address" do + sign_in teacher_user + put "/api/v1/users/#{regular_user.id}/addresses/#{address.id}", params: updated_address_params, headers: { 'Authorization': "Bearer #{@auth_token}" } + expect(response).to have_http_status(:ok) + end + end + + context "when the user is speaker user" do + it "can update an address" do + sign_in speaker_user + put "/api/v1/users/#{regular_user.id}/addresses/#{address.id}", params: updated_address_params, headers: { 'Authorization': "Bearer #{@auth_token}" } + expect(response).to have_http_status(:ok) + end + end end describe "DELETE /destroy" do @@ -99,5 +157,25 @@ expect(response).to have_http_status(:no_content) end end + + context "when the user is teacher user" do + it "can delete an address" do + sign_in teacher_user + expect { + delete "/api/v1/users/#{regular_user.id}/addresses/#{address.id}", headers: { 'Authorization': "Bearer #{@auth_token}" } + }.to change(Address, :count).by(-1) + expect(response).to have_http_status(:no_content) + end + end + + context "when the user is speaker user" do + it "can delete an address" do + sign_in speaker_user + expect { + delete "/api/v1/users/#{regular_user.id}/addresses/#{address.id}", headers: { 'Authorization': "Bearer #{@auth_token}" } + }.to change(Address, :count).by(-1) + expect(response).to have_http_status(:no_content) + end + end end end From 29fd38bd0def05f70a052df41772314d9c829e75 Mon Sep 17 00:00:00 2001 From: Dewi-Anggraini Date: Tue, 14 Jan 2025 10:09:09 +0700 Subject: [PATCH 5/5] added new migration --- db/schema.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 2a82668d..7d32e426 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,6 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2025_01_10_130617) do ActiveRecord::Schema[7.2].define(version: 2025_01_11_165839) do create_table "active_storage_attachments", force: :cascade do |t| t.string "name", null: false @@ -96,6 +95,8 @@ t.datetime "updated_at", null: false t.string "payment_token" t.boolean "canceled", default: false + t.string "stripe_checkout_session_id" + t.string "stripe_payment_intent_id" t.index ["user_id"], name: "index_donations_on_user_id" end @@ -136,9 +137,9 @@ t.string "phone" t.text "comments" t.integer "user_id" + t.string "product_type", null: false + t.integer "product_id", null: false t.integer "address_id" - t.string "product_type" - t.integer "product_id" t.index ["product_type", "product_id"], name: "index_orders_on_product" t.index ["user_id"], name: "index_orders_on_user_id" end