Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/release/14.0' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
ulferts committed Apr 15, 2024
2 parents 5f1ac31 + 77789e0 commit 687ed9c
Show file tree
Hide file tree
Showing 19 changed files with 355 additions and 36 deletions.
Expand Up @@ -79,8 +79,8 @@ def delete_action_item(menu)
scheme: :danger,
href: admin_settings_project_custom_field_path(@project_custom_field),
form_arguments: {
method: :delete, data: { confirm: t("text_are_you_sure"), "turbo-stream": true,
test_selector: "project-custom-field-delete" }
method: :delete, data: { confirm: t("text_are_you_sure_with_project_custom_fields"),
"turbo-stream": true, test_selector: "project-custom-field-delete" }
}) do |item|
item.with_leading_visual_icon(icon: :trash)
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/work_package/journalized.rb
Expand Up @@ -81,7 +81,7 @@ def self.event_url
register_journal_formatted_fields(:fraction,
"estimated_hours", "derived_estimated_hours",
"remaining_hours", "derived_remaining_hours")
register_journal_formatted_fields(:percentage, "done_ratio")
register_journal_formatted_fields(:percentage, "done_ratio", "derived_done_ratio")
register_journal_formatted_fields(:diff, "description")
register_journal_formatted_fields(:schedule_manually, "schedule_manually")
register_journal_formatted_fields(:attachment, /attachments_?\d+/)
Expand Down
2 changes: 1 addition & 1 deletion app/services/journals/create_service.rb
Expand Up @@ -48,7 +48,7 @@ def initialize(journable, user)
self.journable = journable
end

def call(notes: '', cause: {})
def call(notes: "", cause: {})
# JSON columns read from the database always have string keys. As we do not know what is passed in here,
# and we want to compare it to values read from the DB, we need to stringify the keys here as well
normalized_cause = cause.deep_stringify_keys
Expand Down
10 changes: 6 additions & 4 deletions app/workers/work_packages/apply_statuses_p_complete_job.rb
Expand Up @@ -88,10 +88,12 @@ def journal_cause_from(cause_type, status_name:, status_id:, change:)
end

def with_temporary_progress_table
create_temporary_progress_table
yield
ensure
drop_temporary_progress_table
WorkPackage.transaction do
create_temporary_progress_table
yield
ensure
drop_temporary_progress_table
end
end

def create_temporary_progress_table
Expand Down
13 changes: 8 additions & 5 deletions app/workers/work_packages/update_progress_job.rb
Expand Up @@ -60,10 +60,12 @@ def perform(current_mode:, previous_mode:)
private

def with_temporary_progress_table
create_temporary_progress_table
yield
ensure
drop_temporary_progress_table
WorkPackage.transaction do
create_temporary_progress_table
yield
ensure
drop_temporary_progress_table
end
end

def create_temporary_progress_table
Expand Down Expand Up @@ -242,7 +244,8 @@ def copy_progress_values_to_work_packages

def create_journals_for_updated_work_packages(updated_work_package_ids)
WorkPackage.where(id: updated_work_package_ids).find_each do |work_package|
Journals::CreateService.new(work_package, system_user)
Journals::CreateService
.new(work_package, system_user)
.call(cause: { type: "system_update", feature: "progress_calculation_changed" })
end
end
Expand Down
1 change: 1 addition & 0 deletions config/locales/en.yml
Expand Up @@ -3272,6 +3272,7 @@ Project attributes and sections are defined in the <a href=%{admin_settings_url}
text_are_you_sure: "Are you sure?"
text_are_you_sure_continue: "Are you sure you want to continue?"
text_are_you_sure_with_children: "Delete work package and all child work packages?"
text_are_you_sure_with_project_custom_fields: "Deleting this attribute will also delete its values in all projects. Are you sure you want to do this?"
text_assign_to_project: "Assign to the project"
text_form_configuration: >
You can customize which fields will be displayed in work package forms.
Expand Down
69 changes: 60 additions & 9 deletions db/migrate/20231123111357_create_custom_field_sections.rb
@@ -1,4 +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.
#++

require_relative "migration_utils/utils"

class CreateCustomFieldSections < ActiveRecord::Migration[7.0]
include ::Migration::Utils

def up
create_table :custom_field_sections do |t|
t.integer :position
Expand All @@ -23,14 +55,33 @@ def down
private

def create_and_assign_default_section
# for project custom fields only
section = ProjectCustomFieldSection.create!(
name: "Project attributes"
)

# trigger acts_as_list callbacks via updating each record instead of bulk update
ProjectCustomField.find_each do |project_custom_field|
project_custom_field.update!(custom_field_section_id: section.id)
end
create_section_sql = <<~SQL.squish
INSERT INTO "custom_field_sections" ("position", "name", "type", "created_at", "updated_at")
VALUES (:position, :name, :type, :created_at, :updated_at)
RETURNING "id"
SQL

now = Time.current

insert_result =
execute_sql create_section_sql, type: "ProjectCustomFieldSection", name: "Project attributes",
position: 1, created_at: now, updated_at: now

update_sql = <<~SQL.squish
UPDATE "custom_fields"
SET
"position_in_custom_field_section" = "mapping"."new_position",
"custom_field_section_id" = :section_id
FROM (
SELECT
id,
ROW_NUMBER() OVER (ORDER BY updated_at) AS new_position
FROM "custom_fields"
WHERE "custom_fields"."type" = 'ProjectCustomField'
) AS "mapping"
WHERE "custom_fields"."id" = "mapping"."id";
SQL

execute_sql(update_sql, section_id: insert_result.first["id"])
end
end
2 changes: 1 addition & 1 deletion db/migrate/20240402072213_update_progress_calculation.rb
Expand Up @@ -8,7 +8,7 @@ def up
current_mode = "field"
end

perform_method = Rails.env.production? ? :perform_later : :perform_now
perform_method = Rails.env.development? ? :perform_now : :perform_later
WorkPackages::UpdateProgressJob.public_send(perform_method, current_mode:, previous_mode:)
end

Expand Down
Expand Up @@ -63,7 +63,7 @@ As a quick-start, you can use this command to start clamav with local volume mou
```bash
docker run -it --rm \
--name clamav \
--publish 3310
--publish 3310 \
--mount source=clam_db,target=/var/lib/clamav \
clamav/clamav:stable_base
```
Expand Down
Expand Up @@ -82,6 +82,9 @@ https://myopenproject.com/webhooks/gitlab?key=4221687468163843
> - Merge request events
> - Pipeline events
>**Note**: If you are in a local network you might need to allow requests to the local network in your GitLab instance.
>You can find this settings in the **Outbound requests** section when you navigate to **Admin area -> Settings -> Network**.
We recommend that you enable the **SSL verification** before you **Add webhook**.

Now the integration is set up on both sides and you can use it.
Expand Down
Expand Up @@ -168,10 +168,15 @@ def item_path(item_id)
end

def log_error(error)
OpenProject.logger.warn({ command: error.data.source,
message: error.log_message,
data: { status: error.data.payload.status,
body: error.data.payload.body.to_s } })
payload = error.data.payload
OpenProject.logger.warn(
command: error.data.source,
message: error.log_message,
data: {
status: payload.try(:status),
body: (payload.try(:body) || payload).to_s
}
)
end
end
end
Expand Down
Expand Up @@ -136,13 +136,17 @@ def remote_folders_map
using_admin_token do |http|
response = http.get("/v1.0/drives/#{@storage.drive_id}/root/children")

if response.status == 200
case response
in { status: 200 }
ServiceResult.success(result: filter_folders_from(response.json(symbolize_keys: true)))
else
errors = ::Storages::StorageError.new(code: response.status,
data: ::Storages::StorageErrorData.new(
source: self.class, payload: response
))
errors = ::Storages::StorageError.new(
code: response.try(:status),
data: ::Storages::StorageErrorData.new(
source: self.class,
payload: response
)
)
format_and_log_error(errors)
ServiceResult.failure(result: :error, errors:)
end
Expand Down
Expand Up @@ -140,6 +140,22 @@
expect(current_roles).not_to include("read")
end
end

context "when there is a timeout" do
it "logs a warnig and does not raise NoMethodError", vcr: "one_drive/set_permissions_delete_permission_read" do
stub_request_with_timeout(:post, /invite$/)
allow(OpenProject.logger).to receive(:warn)

permissions_command.call(path:, permissions: { read: ["d6e00f6d-1ae7-43e6-b0af-15d99a56d4ce"] })

expect(OpenProject.logger)
.to have_received(:warn)
.with(command: described_class,
message: nil,
data: { body: "/usr/local/bundle/gems/httpx-1.2.3/lib/httpx/response.rb:260:in `full_message': timed out while waiting on select (HTTPX::ConnectTimeoutError)\n",
status: nil }).once
end
end
end

private
Expand Down
Expand Up @@ -241,13 +241,13 @@
before { allow(OpenProject.logger).to receive(:warn) }

context "when reading the root folder fails" do
before { storage.update(drive_id: "THIS-IS-NOT-A-DRIVE-ID") }

it "returns a failure in case retrieving the root list fails", vcr: "one_drive/sync_service_root_read_failure" do
storage.update(drive_id: "THIS-IS-NOT-A-DRIVE-ID")
expect(service.call).to be_failure
end

it "logs the occurrence", vcr: "one_drive/sync_service_root_read_failure" do
storage.update(drive_id: "THIS-IS-NOT-A-DRIVE-ID")
service.call

expect(OpenProject.logger)
Expand All @@ -256,6 +256,18 @@
message: nil,
data: { status: 400, body: /drive id/ })
end

it "does not break in case of timeout", vcr: "one_drive/sync_service_root_read_failure" do
stub_request_with_timeout(:get, /\/root\/children$/)

expect(service.call).to be_failure

expect(OpenProject.logger)
.to have_received(:warn)
.with(command: described_class,
message: nil,
data: "timed out while waiting on select")
end
end

context "when folder creation fails" do
Expand Down
72 changes: 72 additions & 0 deletions spec/features/activities/work_package_activity_spec.rb
@@ -0,0 +1,72 @@
#-- 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.
#++

require "spec_helper"

RSpec.describe "Work package activity", :js, :with_cuprite do
shared_let(:admin) { create(:admin) }
shared_let(:project) { create(:project) }

before_all do
set_factory_default(:user, admin)
set_factory_default(:project, project)
set_factory_default(:project_with_types, project)
end

let_work_packages(<<~TABLE)
hierarchy | work | remaining work | % complete | ∑ work | ∑ remaining work | ∑ % complete
parent | | | | | |
child | 10h | 3h | 70% | 10h | 3h | 70%
TABLE

let(:parent_page) { Pages::FullWorkPackage.new(parent) }

current_user { admin }

context "when the progress values are changed" do
before do
wp_page = Pages::FullWorkPackage.new(parent)
wp_page.visit!
wp_page.update_attributes estimatedTime: "10" # rubocop:disable Rails/ActiveRecordAliases
wp_page.expect_and_dismiss_toaster(message: "Successful update.")
wp_page.update_attributes remainingTime: "5" # rubocop:disable Rails/ActiveRecordAliases
wp_page.expect_and_dismiss_toaster(message: "Successful update.")
end

it "displays changed attributes in the activity tab" do
within("activity-entry", text: admin.name) do
expect(page).to have_list_item(text: "% Complete set to 50%")
expect(page).to have_list_item(text: "Work set to 10.00")
expect(page).to have_list_item(text: "Remaining work set to 5.00")
expect(page).to have_list_item(text: "Total work set to 20.00")
expect(page).to have_list_item(text: "Total remaining work set to 8.00")
expect(page).to have_list_item(text: "Total % complete set to 60%")
end
end
end
end

0 comments on commit 687ed9c

Please sign in to comment.