Skip to content

Commit

Permalink
feat[Op#59435]: define reminders contract
Browse files Browse the repository at this point in the history
  • Loading branch information
akabiru committed Nov 20, 2024
1 parent c8c015e commit 745d3f8
Show file tree
Hide file tree
Showing 8 changed files with 355 additions and 1 deletion.
73 changes: 73 additions & 0 deletions app/contracts/reminders/base_contract.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2024 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module Reminders
class BaseContract < ::ModelContract
attribute :creator_id
attribute :remindable_id
attribute :remindable_type
attribute :remind_at
attribute :notes

validate :validate_creator_exists
validate :validate_acting_user
validate :validate_remindable_exists
validate :validate_manage_reminders_permissions
validate :validate_remind_at_is_in_future

def self.model = Reminder

private

def validate_creator_exists
errors.add :creator, :not_found unless User.exists?(model.creator_id)
end

def validate_acting_user
errors.add :creator, :invalid unless model.creator_id == user.id
end

def validate_remindable_exists
errors.add :remindable, :not_found if model.remindable.blank?
end

def validate_remind_at_is_in_future
if model.remind_at.present? && model.remind_at < Time.current
errors.add :remind_at, :datetime_must_be_in_future
end
end

def validate_manage_reminders_permissions
return if errors.added?(:remindable, :not_found)

unless user.allowed_in_project?(:manage_own_reminders, model.remindable.project)
errors.add :base, :error_unauthorized
end
end
end
end
32 changes: 32 additions & 0 deletions app/contracts/reminders/create_contract.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2024 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module Reminders
class CreateContract < BaseContract
end
end
36 changes: 36 additions & 0 deletions app/contracts/reminders/delete_contract.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2024 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module Reminders
class DeleteContract < ::DeleteContract
delete_permission -> {
# The user can delete the reminder if they created it
model.creator_id == user.id
}
end
end
5 changes: 5 additions & 0 deletions config/initializers/permissions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@
{},
permissible_on: :project_query,
require: :loggedin

map.permission :manage_own_reminders,
{},
permissible_on: :project,
require: :member
end

map.project_module :work_package_tracking, order: 90 do |wpt|
Expand Down
3 changes: 2 additions & 1 deletion config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,7 @@ en:
not_an_integer: "is not an integer."
not_an_iso_date: "is not a valid date. Required format: YYYY-MM-DD."
not_same_project: "doesn't belong to the same project."
datetime_must_be_in_future: "must be in the future."
odd: "must be odd."
regex_match_failed: "does not match the regular expression %{expression}."
regex_invalid: "could not be validated with the associated regular expression."
Expand Down Expand Up @@ -3490,7 +3491,7 @@ en:
If this option is active, login requests will redirect to the configured omniauth provider.
The login dropdown and sign-in page will be disabled.
<br/>
<strong>Note:</strong> Unless you also disable password logins, with this option enabled,
<strong>Note:</strong> Unless you also disable password logins, with this option enabled,
users can still log in internally by visiting the <code>%{internal_path}</code> login page.
attachments:
whitelist_text_html: >
Expand Down
123 changes: 123 additions & 0 deletions spec/contracts/reminders/base_contract_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

require "spec_helper"
require "contracts/shared/model_contract_shared_context"

RSpec.describe Reminders::BaseContract do
include_context "ModelContract shared context"

let(:contract) { described_class.new(reminder, user) }
let(:user) { build_stubbed(:admin) }
let(:creator) { user }
let(:reminder) { build_stubbed(:reminder, creator:) }

before do
User.current = user
allow(User).to receive(:exists?).with(user.id).and_return(true)
end

describe "admin user" do
it_behaves_like "contract is valid"
end

describe "non-admin user" do
context "with valid permissions" do
let(:user) { build_stubbed(:user) }

before do
mock_permissions_for(user) do |mock|
mock.allow_in_project(:manage_own_reminders, project: reminder.remindable.project)
end
end

it_behaves_like "contract is valid"
end

context "without valid permissions" do
let(:user) { build_stubbed(:user) }

it_behaves_like "contract is invalid", base: :error_unauthorized
end
end

describe "validate creator exists" do
context "when creator does not exist" do
before { allow(User).to receive(:exists?).with(user.id).and_return(false) }

it_behaves_like "contract is invalid", creator: :not_found
end
end

describe "validate acting user" do
context "when the current user is different from the remindable acting user" do
let(:different_user) { build_stubbed(:user) }

before do
allow(User).to receive(:exists?).with(different_user.id).and_return(true)
reminder.creator = different_user
end

it_behaves_like "contract is invalid", creator: :invalid
end
end

describe "validate remindable object" do
context "when remindable is blank" do
before { reminder.remindable = nil }

it_behaves_like "contract is invalid", remindable: :not_found
end

context "when remindable is a work package" do
let(:work_package) { build_stubbed(:work_package) }

before { reminder.remindable = work_package }

it_behaves_like "contract is valid"
end
end

describe "validate remind at is in future" do
context "when remind at is in the past" do
before { reminder.remind_at = 1.day.ago }

it_behaves_like "contract is invalid", remind_at: :datetime_must_be_in_future
end

context "when remind at is in the future" do
before { reminder.remind_at = 1.day.from_now }

it_behaves_like "contract is valid"
end
end

include_examples "contract reuses the model errors"
end
48 changes: 48 additions & 0 deletions spec/contracts/reminders/delete_contract_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

require "spec_helper"
require "contracts/shared/model_contract_shared_context"

RSpec.describe Reminders::DeleteContract do
include_context "ModelContract shared context"

let(:contract) { described_class.new(reminder, current_user) }
let(:current_user) { build_stubbed(:admin) }
let(:reminder) { build_stubbed(:reminder, creator: current_user) }

context "when user is different from the one that created the reminder" do
let(:another_user) { build_stubbed(:admin) }

before { reminder.creator = another_user }

it_behaves_like "contract user is unauthorized"
end

include_examples "contract reuses the model errors"
end
36 changes: 36 additions & 0 deletions spec/factories/reminders_factory.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

FactoryBot.define do
factory :reminder do
remindable factory: :work_package_journal
creator factory: :user
remind_at { 1.day.from_now }
notes { "This is a reminder" }
end
end

0 comments on commit 745d3f8

Please sign in to comment.