diff --git a/app/controllers/api/v1/users_controller.rb b/app/controllers/api/v1/users_controller.rb index 83f60d3..c1db34c 100644 --- a/app/controllers/api/v1/users_controller.rb +++ b/app/controllers/api/v1/users_controller.rb @@ -2,7 +2,7 @@ class Api::V1::UsersController < ApiController def index - users = policy_scope(User) + users = policy_scope(User).kept authorize users render json: apply_fetcheable(users).to_blueprint end @@ -21,18 +21,18 @@ def create if user.save render json: user.to_blueprint, status: :created else - render json: { errors: user.errors.full_messages }, status: :unprocessable_entity + render_validation_error(user) end end def destroy - user = policy_scope(User).find(params[:id]) + user = policy_scope(User).kept.find(params[:id]) authorize user - if user.destroy - render json: { message: 'User deleted' }, status: :ok + if current_user.discard + head :no_content else - render json: { errors: user.errors.full_messages }, status: :unprocessable_entity + render_validation_error(user) end end diff --git a/app/models/request.rb b/app/models/request.rb index 05db772..8bf5be9 100644 --- a/app/models/request.rb +++ b/app/models/request.rb @@ -23,7 +23,7 @@ class Request < ApplicationRecord # Associations belongs_to :handler, - class_name: 'Users::Grower', + class_name: 'User', inverse_of: :handled_requests, optional: true diff --git a/app/models/user.rb b/app/models/user.rb index b3d25d1..37436f5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -32,8 +32,14 @@ class User < ApplicationRecord foreign_key: :resource_owner_id, dependent: :destroy + has_many :handled_requests, + class_name: 'Request', + foreign_key: 'handler_id', + inverse_of: :handler, + dependent: :destroy + # Callbacks - after_discard :anonymize + after_discard :partial_anonymize # check password_confirmation before using `update_password` method from Clearance::User # @@ -46,16 +52,13 @@ def update_password!(password:, password_confirmation:) update_password(password) end - # Private instance methods private - def anonymize + def partial_anonymize self.email = "anonymized_#{id}" self.encrypted_password = 'anonymized' self.confirmation_token = nil self.remember_token = 'anonymized' - self.first_name = nil - self.last_name = nil save(validate: false) end @@ -70,8 +73,8 @@ def anonymize # encrypted_password :string(128) not null # confirmation_token :string(128) # remember_token :string(128) not null -# first_name :string -# last_name :string +# first_name :string not null +# last_name :string not null # created_at :datetime not null # updated_at :datetime not null # discarded_at :datetime diff --git a/app/models/users/grower.rb b/app/models/users/grower.rb deleted file mode 100644 index 2b99557..0000000 --- a/app/models/users/grower.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -class Users::Grower < User - # Associations - has_many :handled_requests, - class_name: 'Request', - foreign_key: 'handler_id', - inverse_of: :handler, - dependent: :destroy -end - -# == Schema Information -# -# Table name: users -# -# id :bigint not null, primary key -# email :string not null -# encrypted_password :string(128) not null -# confirmation_token :string(128) -# remember_token :string(128) not null -# first_name :string -# last_name :string -# created_at :datetime not null -# updated_at :datetime not null -# discarded_at :datetime -# -# Indexes -# -# index_users_on_discarded_at (discarded_at) -# index_users_on_email (email) UNIQUE -# index_users_on_remember_token (remember_token) -# diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index 60d2325..78ab470 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -9,11 +9,11 @@ def show? true end - def create? + def update? true end - def update? + def create? true end diff --git a/config/locales/enumerize.fr.yml b/config/locales/enumerize.fr.yml index 6df5755..941b4a2 100644 --- a/config/locales/enumerize.fr.yml +++ b/config/locales/enumerize.fr.yml @@ -1,13 +1,5 @@ fr: enumerize: - user: - role: - requester: demandeur - grower: serriste - invite: - role: - requester: demandeur - grower: serriste pot: shape: square: carré diff --git a/db/migrate/20241217133422_first_name_last_name_not_null.rb b/db/migrate/20241217133422_first_name_last_name_not_null.rb new file mode 100644 index 0000000..92803cf --- /dev/null +++ b/db/migrate/20241217133422_first_name_last_name_not_null.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class FirstNameLastNameNotNull < ActiveRecord::Migration[7.2] + def up + change_table :users, bulk: true do |t| + t.change :first_name, :string, null: false + t.change :last_name, :string, null: false + end + end + + def down + change_table :users, bulk: true do |t| + t.change :first_name, :string, null: true + t.change :last_name, :string, null: true + end + end +end diff --git a/db/schema.rb b/db/schema.rb index fd14fd9..66edc20 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: 2024_12_16_104204) do +ActiveRecord::Schema[7.2].define(version: 2024_12_17_133422) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -152,8 +152,8 @@ t.string "encrypted_password", limit: 128, null: false t.string "confirmation_token", limit: 128 t.string "remember_token", limit: 128, null: false - t.string "first_name" - t.string "last_name" + t.string "first_name", null: false + t.string "last_name", null: false t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false t.datetime "discarded_at", precision: nil diff --git a/db/seeds.rb b/db/seeds.rb index 95ef205..3571d16 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -13,7 +13,7 @@ confidential: false ) - Users::Grower.create!( + User.create!( email: 'dev+grower@progeser.com', password: 'password', password_confirmation: 'password', @@ -130,7 +130,7 @@ requester_last_name: 'Doe', requester_email: 'john.doe@example.com', laboratory: 'My lab', - handler: Users::Grower.first, + handler: User.first, plant_stage: Plant.first.plant_stages.last, name: 'My first request', plant_name: Plant.first.name, diff --git a/erd.pdf b/erd.pdf index 2aa7dba..84de68f 100644 Binary files a/erd.pdf and b/erd.pdf differ diff --git a/spec/acceptance/api/v1/me_controller_spec.rb b/spec/acceptance/api/v1/me_controller_spec.rb index fd741fa..c9c8ce4 100644 --- a/spec/acceptance/api/v1/me_controller_spec.rb +++ b/spec/acceptance/api/v1/me_controller_spec.rb @@ -8,7 +8,9 @@ header 'Accept', 'application/json' header 'Content-Type', 'application/json' - let!(:user) { users(:user1) } + let!(:user) { users(:user1) } + let!(:user_last_name) { user.last_name } + let!(:user_first_name) { user.first_name } let!(:user_token) { Doorkeeper::AccessToken.create!(resource_owner_id: user.id) } get '/api/v1/me' do @@ -59,8 +61,8 @@ expect(user.discarded?).to be(true) expect(user.email).to eq('anonymized_1') expect(user.encrypted_password).to eq('anonymized') - expect(user.first_name).to be_nil - expect(user.last_name).to be_nil + expect(user.first_name).to eq(user_first_name) + expect(user.last_name).to eq(user_last_name) end end end diff --git a/spec/acceptance/api/v1/users_controller_spec.rb b/spec/acceptance/api/v1/users_controller_spec.rb index 3a3df53..8c0d9de 100644 --- a/spec/acceptance/api/v1/users_controller_spec.rb +++ b/spec/acceptance/api/v1/users_controller_spec.rb @@ -40,8 +40,8 @@ expect(status).to eq(200) - expect(response_body).to eq(User.to_blueprint) - expect(JSON.parse(response_body).count).to eq(User.count) + expect(response_body).to eq(User.kept.to_blueprint) + expect(JSON.parse(response_body).count).to eq(User.kept.count) end end @@ -101,8 +101,7 @@ do_request - expect(status).to eq(200) - expect(JSON.parse(response_body)['message']).to eq('User deleted') + expect(status).to eq(204) end end end diff --git a/spec/requests/api/v1/users_spec.rb b/spec/requests/api/v1/users_spec.rb index 3c7380a..5fbb0ce 100644 --- a/spec/requests/api/v1/users_spec.rb +++ b/spec/requests/api/v1/users_spec.rb @@ -23,8 +23,8 @@ expect(response.parsed_body.count).to eq(2) expect(response.headers['Pagination-Current-Page']).to eq(1) expect(response.headers['Pagination-Per']).to eq(2) - expect(response.headers['Pagination-Total-Pages']).to eq(2) - expect(response.headers['Pagination-Total-Count']).to eq(3) + expect(response.headers['Pagination-Total-Pages']).to eq(1) + expect(response.headers['Pagination-Total-Count']).to eq(2) end end end @@ -33,7 +33,7 @@ describe 'GET api/v1/users/:id' do context 'when 404' do it_behaves_like 'with authenticated grower' do - it 'gets a user' do + it 'cannot get a user' do get( '/api/v1/users/0', headers: @@ -45,7 +45,7 @@ end context 'when 401' do - it 'gets a user' do + it 'cannot get a user' do get( '/api/v1/users/0', headers: @@ -127,13 +127,11 @@ context 'when 422' do it_behaves_like 'with authenticated grower' do it 'fails to delete user' do - allow_any_instance_of(User).to receive(:destroy).and_return(false) - + allow_any_instance_of(User).to receive(:discard).and_return(false) delete( '/api/v1/users/1', headers: ) - expect(status).to eq(422) end end diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml index b29f841..08cbc66 100644 --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -41,8 +41,8 @@ user3: encrypted_password: anonymized confirmation_token: remember_token: anonymized - last_name: - first_name: + last_name: Anonymized + first_name: Anonymized created_at: !ruby/object:ActiveSupport::TimeWithZone utc: &1 2019-10-31 15:40:41.545001000 Z zone: &2 !ruby/object:ActiveSupport::TimeZone @@ -66,8 +66,8 @@ user3: # encrypted_password :string(128) not null # confirmation_token :string(128) # remember_token :string(128) not null -# first_name :string -# last_name :string +# first_name :string not null +# last_name :string not null # created_at :datetime not null # updated_at :datetime not null # discarded_at :datetime diff --git a/test/models/user_test.rb b/test/models/user_test.rb index 16d3609..8f1bd0b 100644 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -3,9 +3,52 @@ require 'test_helper' class UserTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end + # Setup + def setup + @user = users(:user2) + end + + # Validations + test 'valid user' do + assert @user.valid?, @user.errors.messages + end + + test 'invalid without email' do + @user.email = nil + assert_not @user.valid? + assert_not_empty @user.errors[:email] + end + + test 'invalid with existing email' do + @user.email = users(:user1).email + assert_not @user.valid? + assert_not_empty @user.errors[:email] + end + + test 'invalid with incorrect email value' do + @user.email = 'foo' + assert_not @user.valid? + assert_not_empty @user.errors[:email] + end + + test 'invalid without password' do + @user.password = nil + @user.encrypted_password = nil + assert_not @user.valid? + assert_not_empty @user.errors[:password] + end + + test 'invalid without first_name' do + @user.first_name = nil + assert_not @user.valid? + assert_not_empty @user.errors[:first_name] + end + + test 'invalid without last_name' do + @user.last_name = nil + assert_not @user.valid? + assert_not_empty @user.errors[:last_name] + end end # == Schema Information @@ -17,8 +60,8 @@ class UserTest < ActiveSupport::TestCase # encrypted_password :string(128) not null # confirmation_token :string(128) # remember_token :string(128) not null -# first_name :string -# last_name :string +# first_name :string not null +# last_name :string not null # created_at :datetime not null # updated_at :datetime not null # discarded_at :datetime diff --git a/test/models/users/grower_test.rb b/test/models/users/grower_test.rb deleted file mode 100644 index b54204e..0000000 --- a/test/models/users/grower_test.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class Users::GrowerTest < ActiveSupport::TestCase - # Setups - def setup - @user = users(:user2) - end - - # Validations - test 'valid user' do - assert @user.valid?, @user.errors.messages - end - - test 'invalid without email' do - @user.email = nil - assert_not @user.valid? - assert_not_empty @user.errors[:email] - end - - test 'invalid with existing email' do - @user.email = users(:user1).email - assert_not @user.valid? - assert_not_empty @user.errors[:email] - end - - test 'invalid with incorrect email value' do - @user.email = 'foo' - assert_not @user.valid? - assert_not_empty @user.errors[:email] - end - - test 'invalid without password' do - @user.password = nil - @user.encrypted_password = nil - assert_not @user.valid? - assert_not_empty @user.errors[:password] - end - - test 'invalid without first_name' do - @user.first_name = nil - assert_not @user.valid? - assert_not_empty @user.errors[:first_name] - end - - test 'invalid without last_name' do - @user.last_name = nil - assert_not @user.valid? - assert_not_empty @user.errors[:last_name] - end -end - -# == Schema Information -# -# Table name: users -# -# id :bigint not null, primary key -# email :string not null -# encrypted_password :string(128) not null -# confirmation_token :string(128) -# remember_token :string(128) not null -# first_name :string -# last_name :string -# created_at :datetime not null -# updated_at :datetime not null -# discarded_at :datetime -# -# Indexes -# -# index_users_on_discarded_at (discarded_at) -# index_users_on_email (email) UNIQUE -# index_users_on_remember_token (remember_token) -#