[#55382] add connection validation section

- https://community.openproject.org/work_packages/55382
- added new controller for connection validation
- added new component to sidebar
This commit is contained in:
Eric Schubert
2024-06-07 12:39:26 +02:00
parent 25f6994ce1
commit 71dbd861b3
10 changed files with 352 additions and 2 deletions
+1
View File
@@ -210,6 +210,7 @@ gem "validate_url"
# Storages support code
gem "dry-container"
gem "dry-monads"
# ActiveRecord extension which adds typecasting to store accessors
gem "store_attribute", "~> 1.0"
+5
View File
@@ -476,6 +476,10 @@ GEM
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-monads (1.6.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-types (1.7.2)
bigdecimal (~> 3.0)
concurrent-ruby (~> 1.0)
@@ -1195,6 +1199,7 @@ DEPENDENCIES
doorkeeper (~> 5.7.0)
dotenv-rails
dry-container
dry-monads
email_validator (~> 2.2.3)
equivalent-xml (~> 0.6)
erb_lint
@@ -0,0 +1,68 @@
<%#-- 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.
++#%>
<%=
component_wrapper do
flex_layout do |container|
container.with_row do
flex_layout do |heading|
heading.with_row do
render(Primer::Beta::Heading.new(tag: :h4)) { I18n.t("storages.connection_validation.title") }
end
end
end
container.with_row(mt: 2) do
primer_form_with(
model: @storage,
url: validate_connection_admin_settings_storage_connection_validation_path(@storage),
method: :post,
data: { turbo: true }
) do
flex_layout do |form|
form.with_row do
render(OpTurbo::FrameComponent.new(id: :connection_validation_result))
end
form.with_row(mt: 2) do
render(Primer::Beta::Button.new(
scheme: :default,
block: :true,
type: :submit,
)) do |button|
button.with_leading_visual_icon(icon: "tasklist")
I18n.t("storages.connection_validation.action")
end
end
end
end
end
end
end
%>
@@ -0,0 +1,45 @@
# frozen_string_literal: true
#-- 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 Storages
module Admin
module Sidebar
class ConnectionValidationComponent < ApplicationComponent # rubocop:disable OpenProject/AddPreviewForViewComponent
include OpTurbo::Streamable
include OpPrimer::ComponentHelpers
def initialize(storage:)
super(storage)
@storage = storage
end
end
end
end
end
@@ -1,6 +1,7 @@
<%=
component_wrapper do
render(Primer::OpenProject::BorderGrid.new) do |border_grid|
border_grid.with_row { render(Storages::Admin::Sidebar::ConnectionValidationComponent.new(storage: @storage)) }
border_grid.with_row { render(Storages::Admin::Sidebar::HealthStatusComponent.new(storage: @storage)) }
border_grid.with_row { render(Storages::Admin::Sidebar::HealthNotificationsComponent.new(storage: @storage)) }
end
@@ -0,0 +1,148 @@
# frozen_string_literal: true
#-- 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 Storages
module Admin
class ConnectionValidationController < ApplicationController
include OpTurbo::ComponentStream
include Dry::Monads[:maybe]
using Peripherals::ServiceResultRefinements
layout "admin"
before_action :require_admin
model_object OneDriveStorage
before_action :find_model_object, only: %i[validate_connection]
def validate_connection
@result = maybe_is_not_configured
.or { drive_id_wrong }
.or { tenant_id_wrong }
.or { client_id_wrong }
.or { client_secret_wrong }
.or { request_failed_with_unknown_error }
.value_or(ConnectionValidation.new(icon: "check-circle",
scheme: :success,
description: I18n.t("storages.connection_validation.success")))
respond_to do |format|
format.turbo_stream
end
end
private
def query
@query ||= Peripherals::Registry
.resolve("#{@storage.short_provider_type}.queries.files")
.call(storage: @storage, auth_strategy:, folder: root_folder)
end
def maybe_is_not_configured
return None() if @storage.configured?
Some(ConnectionValidation.new(icon: :alert,
scheme: :warning,
description: I18n.t("storages.connection_validation.not_configured")))
end
def drive_id_wrong
return None() if query.result != :not_found
Some(ConnectionValidation.new(icon: :skip,
scheme: :danger,
description: I18n.t("storages.connection_validation.drive_id_wrong")))
end
def tenant_id_wrong
return None() if query.result != :unauthorized
payload = JSON.parse(query.error_payload)
return None() if payload["error"] != "invalid_request"
tenant_id_string = "Tenant '#{@storage.tenant_id}' not found."
return None() unless payload["error_description"].include?(tenant_id_string)
Some(ConnectionValidation.new(icon: :skip,
scheme: :danger,
description: I18n.t("storages.connection_validation.tenant_id_wrong")))
end
def client_id_wrong
return None() if query.result != :unauthorized
payload = JSON.parse(query.error_payload)
return None() if payload["error"] != "unauthorized_client"
Some(ConnectionValidation.new(icon: :skip,
scheme: :danger,
description: I18n.t("storages.connection_validation.client_id_wrong")))
end
def client_secret_wrong
return None() if query.result != :unauthorized
payload = JSON.parse(query.error_payload)
return None() if payload["error"] != "invalid_client"
Some(ConnectionValidation.new(icon: :skip,
scheme: :danger,
description: I18n.t("storages.connection_validation.client_secret_wrong")))
end
def request_failed_with_unknown_error
return None() if query.success?
Rails.logger.error("Connection validation failed with unknown error:\n\t" \
"status: #{query.result}\n\tresponse: #{query.error_payload}")
Some(ConnectionValidation.new(icon: :skip,
scheme: :danger,
description: I18n.t("storages.connection_validation.unknown_error")))
end
def find_model_object(object_id = :storage_id)
super
@storage = @object
end
def root_folder
Peripherals::ParentFolder.new("/")
end
def auth_strategy
Peripherals::Registry.resolve("#{@storage.short_provider_type}.authentication.userless").call
end
end
end
end
@@ -0,0 +1,33 @@
# frozen_string_literal: true
#-- 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 Storages
ConnectionValidation = Data.define(:icon, :scheme, :description)
end
@@ -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.
++#%>
<%= turbo_stream.update :connection_validation_result do %>
<%= render(Primer::Alpha::Banner.new(icon: @result.icon, scheme: @result.scheme)) { @result.description } %>
<% end %>
+10
View File
@@ -116,6 +116,16 @@ en:
subscribe: Subscribe
title: Email notifications
unsubscribe: Unsubscribe
connection_validation:
title: Connection validation
action: Validate connection
not_configured: The connection could not be validated. Please finish configuration first.
drive_id_wrong: The configured drive id could not be found. Please check the configuration.
tenant_id_wrong: The configured directory (tenant) id is invalid. Please check the configuration.
client_id_wrong: The configured OAuth 2 client id is invalid. Please check the configuration.
client_secret_wrong: The configured OAuth 2 client secret is invalid. Please check the configuration.
unknown_error: The connection could not be validated. An unknown error occurred. Please check the server logs for further information.
success: The connection works as expected.
help_texts:
project_folder: The project folder is the default folder for file uploads for this project. Users can nevertheless still upload files to other locations.
instructions:
+9 -2
View File
@@ -38,11 +38,18 @@ Rails.application.routes.draw do
post :finish_setup
end
resource :automatically_managed_project_folders, controller: "/storages/admin/automatically_managed_project_folders",
only: %i[new create edit update]
resource :automatically_managed_project_folders,
controller: "/storages/admin/automatically_managed_project_folders",
only: %i[new create edit update]
resource :access_management, controller: "/storages/admin/access_management", only: %i[new create edit update]
resource :connection_validation,
controller: "/storages/admin/connection_validation",
only: [] do
post :validate_connection, on: :member
end
get :select_provider, on: :collection
member do