Skip to content

Commit

Permalink
Refactor the step-by-step storage setup
Browse files Browse the repository at this point in the history
The idea is that now there is a central class (a Wizard)
managing the steps necessary to guide the user through creation
of a new storage.

This avoids problems such as the update action of step A being
required to prepare some parts of step B already, so that the
view/component for step B can be rendered correctly.

Since the same components are used inside and outside of the
step-by-step wizard, we are explicitly transferring the state on
whether we are currently in a wizard via parameters. This results
in some degree of boilerplate, but is hopefully more clear than
guessing based on context, whether or not we should show the next
step after submission or go back to regular edit mode.
  • Loading branch information
NobodysNightmare committed Jan 28, 2025
1 parent 4a79a6b commit 4397711
Show file tree
Hide file tree
Showing 34 changed files with 830 additions and 213 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
render(Primer::Beta::Text.new(tag: :div, test_selector: 'storage-access-management-form')) do
primer_form_with(
model:,
url: admin_settings_storage_access_management_path(storage),
method: form_method
url: form_url,
method: form_method,
data: { turbo_frame: "page-content" }
) do |form|
flex_layout do |access_management_row|
access_management_row.with_row(mb: 3) do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,15 @@ class AccessManagementFormComponent < ApplicationComponent

alias_method :storage, :model

options in_wizard: false

def self.wrapper_key = :access_management_section

def form_url
query = { continue_wizard: storage.id } if in_wizard
admin_settings_storage_access_management_path(storage, query)
end

private

def form_method
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,17 @@ class AutomaticallyManagedProjectFoldersFormComponent < ApplicationComponent

alias_method :storage, :model

options in_wizard: false

def self.wrapper_key = :automatically_managed_project_folders_section

def form_method
options[:form_method] || default_form_method
end

def form_url
options[:form_url] || default_form_url
query = { continue_wizard: storage.id } if in_wizard
admin_settings_storage_automatically_managed_project_folders_path(storage, query)
end

def submit_button_options
Expand Down Expand Up @@ -82,9 +85,5 @@ def default_form_method
def new_record?
storage.automatic_management_new_record?
end

def default_form_url
admin_settings_storage_automatically_managed_project_folders_path(storage)
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
url: form_url,
method: form_method,
data: {
"turbo-frame": "page-content",
controller: "storages--automatically-managed-project-folders-form",
'application-target': "dynamic",
'storages--automatically-managed-project-folders-form-provider-type-value': storage.provider_type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,20 @@ class GeneralInfoFormComponent < ApplicationComponent
alias_method :storage, :model

options form_method: :post,
submit_button_disabled: false
submit_button_disabled: false,
in_wizard: false

def self.wrapper_key = :storage_general_info_section

def form_url
options[:form_url] || default_form_url
query = { continue_wizard: storage.id } if in_wizard

case form_method
when :get, :post
admin_settings_storages_path(query)
when :patch, :put
admin_settings_storage_path(storage, query)
end
end

def submit_button_options
Expand All @@ -60,15 +68,6 @@ def cancel_button_options

private

def default_form_url
case form_method
when :get, :post
admin_settings_storages_path
when :patch, :put
admin_settings_storage_path(storage)
end
end

def cancel_button_path
options.fetch(:cancel_button_path) do
if storage.persisted?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
render(Primer::Beta::Text.new(tag: :div, test_selector: 'storage-oauth-client-form')) do
primer_form_with(
model: oauth_client,
url: admin_settings_storage_oauth_client_path(storage),
method: form_method
url: form_url,
method: form_method,
data: data_attributes
) do |form|
flex_layout do |oauth_client_row|
oauth_client_row.with_row(mb: 3) do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,19 @@ class OAuthClientFormComponent < ApplicationComponent
attr_reader :storage
alias_method :oauth_client, :model

options in_wizard: false

def self.wrapper_key = :storage_oauth_client_section

def initialize(oauth_client:, storage:, **)
super(oauth_client, **)
@storage = storage
end

def self.wrapper_key = :storage_oauth_client_section
def form_url
query = { continue_wizard: storage.id } if in_wizard
admin_settings_storage_oauth_client_path(storage, query)
end

def form_method
options[:form_method] || default_form_method
Expand Down Expand Up @@ -75,5 +82,9 @@ def first_time_configuration?
def default_form_method
first_time_configuration? ? :post : :patch
end

def data_attributes
{ turbo_frame: "page-content" }
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
render(Primer::Beta::Text.new(tag: :div, test_selector: 'storage-oauth-client-form')) do
primer_form_with(
model: oauth_client,
url: finish_setup_admin_settings_storage_oauth_client_path(storage),
url: form_url,
method: :post
) do |form|
flex_layout do |oauth_client_row|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,19 @@ class RedirectUriFormComponent < ApplicationComponent
attr_reader :storage
alias_method :oauth_client, :model

options in_wizard: false

def self.wrapper_key = :storage_redirect_uri_section

def initialize(oauth_client:, storage:, **)
super(oauth_client, **)
@storage = storage
end

def self.wrapper_key = :storage_redirect_uri_section
def form_url
query = { continue_wizard: storage.id } if in_wizard
finish_setup_admin_settings_storage_oauth_client_path(storage, query)
end

def cancel_button_path
storage.persisted? ? edit_admin_settings_storage_path(storage) : admin_settings_storages_path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class OAuthApplicationInfoCopyComponent < ApplicationComponent
attr_reader :storage
alias_method :oauth_application, :model

options in_wizard: false

def initialize(oauth_application:, storage:, **)
super(oauth_application, **)
@storage = storage
Expand All @@ -56,14 +58,16 @@ def submit_button_options
{
scheme: :primary,
tag: :a,
href: submit_button_path
}.merge(options.fetch(:submit_button_options, {}))
href: submit_button_path,
data: { turbo_stream: true, turbo_frame: "page-content" }
}
end

private

def submit_button_path
options[:submit_button_path] || show_oauth_application_admin_settings_storage_path(storage)
query = { continue_wizard: storage.id } if in_wizard
show_oauth_application_admin_settings_storage_path(storage, query)
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

grid.with_area(:description, tag: :div, color: :subtle, test_selector: 'storage-redirect-uri-description') do
concat(render(Primer::Beta::Text.new) { provider_redirect_uri_description })
if storage.oauth_client.present?
if storage.oauth_client&.persisted?
concat(render(Primer::Beta::ClipboardCopy.new('aria-label': I18n.t('storages.instructions.one_drive.copy_redirect_uri'),
value: storage.oauth_client.redirect_uri)))
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
end

component.with_row(scheme: :default) do
if storage.new_record?
render(Storages::Admin::Forms::GeneralInfoFormComponent.new(storage))
if wizard_step == "general_info"
render(Storages::Admin::Forms::GeneralInfoFormComponent.new(storage, in_wizard: true))
else
render(Storages::Admin::GeneralInfoComponent.new(storage))
end
Expand All @@ -16,21 +16,25 @@
component.with_row(scheme: :neutral, color: :muted) do
grid_layout('op-storage-view--row', tag: :div, align_items: :center) do |grid|
grid.with_area(:item, tag: :div, mr: 3) do
render(Primer::Beta::Text.new(font_weight: :semibold, mr: 1)) { I18n.t('storages.file_storage_view.oauth_applications') }
render(Primer::Beta::Text.new(font_weight: :semibold, mr: 1)) { I18n.t('storages.file_storage_view.oauth_configuration') }
end
end
end

component.with_row(scheme: :default) do
if storage.new_record? || openproject_oauth_application_section_closed?
render(Storages::Admin::OAuthApplicationInfoComponent.new(oauth_application:, storage:))
if wizard_step == "oauth_application"
render(Storages::Admin::OAuthApplicationInfoCopyComponent.new(oauth_application:, storage:, in_wizard: true))
else
render(Storages::Admin::OAuthApplicationInfoCopyComponent.new(oauth_application:, storage:))
render(Storages::Admin::OAuthApplicationInfoComponent.new(oauth_application:, storage:))
end
end

component.with_row(scheme: :default) do
render(Storages::Admin::OAuthClientInfoComponent.new(oauth_client: storage.oauth_client, storage:))
if wizard_step == "oauth_client"
render(Storages::Admin::Forms::OAuthClientFormComponent.new(oauth_client: storage.oauth_client, storage:, in_wizard: true))
else
render(Storages::Admin::OAuthClientInfoComponent.new(oauth_client: storage.oauth_client, storage:))
end
end

component.with_row(scheme: :neutral, color: :muted) do
Expand All @@ -42,10 +46,10 @@
end

component.with_row(scheme: :default) do
if automatically_managed_project_folders_section_closed?
render(Storages::Admin::AutomaticallyManagedProjectFoldersInfoComponent.new(storage))
if wizard_step == "automatically_managed_project_folders"
render(Storages::Admin::Forms::AutomaticallyManagedProjectFoldersFormComponent.new(storage, in_wizard: true))
else
render(Storages::Admin::Forms::AutomaticallyManagedProjectFoldersFormComponent.new(storage))
render(Storages::Admin::AutomaticallyManagedProjectFoldersInfoComponent.new(storage))
end
end
end
Expand All @@ -60,23 +64,35 @@
end

component.with_row(scheme: :default) do
render(Storages::Admin::AccessManagementComponent.new(storage))
if wizard_step == "access_management"
render(Storages::Admin::Forms::AccessManagementFormComponent.new(storage, in_wizard: true))
else
render(Storages::Admin::AccessManagementComponent.new(storage))
end
end

component.with_row(scheme: :neutral, color: :muted) do
grid_layout('op-storage-view--row', tag: :div, align_items: :center) do |grid|
grid.with_area(:item, tag: :div, mr: 3) do
render(Primer::Beta::Text.new(font_weight: :semibold, mr: 1)) { I18n.t('storages.file_storage_view.oauth_applications') }
render(Primer::Beta::Text.new(font_weight: :semibold, mr: 1)) { I18n.t('storages.file_storage_view.oauth_configuration') }
end
end
end

component.with_row(scheme: :default) do
render(Storages::Admin::OAuthClientInfoComponent.new(oauth_client: storage.oauth_client, storage:))
if wizard_step == "oauth_client"
render(Storages::Admin::Forms::OAuthClientFormComponent.new(oauth_client: storage.oauth_client, storage:, in_wizard: true))
else
render(Storages::Admin::OAuthClientInfoComponent.new(oauth_client: storage.oauth_client, storage:))
end
end

component.with_row(scheme: :default) do
render(Storages::Admin::RedirectUriComponent.new(oauth_client: storage.oauth_client, storage:))
if wizard_step == "redirect_uri"
render(Storages::Admin::Forms::RedirectUriFormComponent.new(oauth_client: storage.oauth_client, storage:, in_wizard: true))
else
render(Storages::Admin::RedirectUriComponent.new(oauth_client: storage.oauth_client, storage:))
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,10 @@ class StorageViewComponent < ApplicationComponent
include OpPrimer::ComponentHelpers
include StorageViewInformation

options openproject_oauth_application_section_open: false,
automatically_managed_project_folders_section_open: false
options wizard_step: nil

alias_method :storage, :model
alias_method :openproject_oauth_application_section_open?, :openproject_oauth_application_section_open
alias_method :automatically_managed_project_folders_section_open?, :automatically_managed_project_folders_section_open

delegate :oauth_application, to: :model

def openproject_oauth_application_section_closed?
!openproject_oauth_application_section_open?
end

def automatically_managed_project_folders_section_closed?
!automatically_managed_project_folders_section_open?
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def provider_oauth_client_description
end

def provider_redirect_uri_description
if storage.oauth_client
if storage.oauth_client&.persisted?
"#{I18n.t('storages.label_uri')}: #{storage.oauth_client.redirect_uri}"
else
I18n.t("storages.configuration_checks.redirect_uri_incomplete.#{storage}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,13 @@ def create
service_result = call_update_service

service_result.on_success do
update_via_turbo_stream(component: Storages::Admin::AccessManagementComponent.new(@storage))
update_via_turbo_stream(component: Storages::Admin::Forms::OAuthClientFormComponent.new(
oauth_client: @storage.build_oauth_client, storage: @storage
))
redirect_to(new_admin_settings_storage_path(continue_wizard: @storage.id), status: :see_other)
end

service_result.on_failure do
update_via_turbo_stream(component: Storages::Admin::Forms::AccessManagementFormComponent.new(@storage))
update_via_turbo_stream(component: Storages::Admin::Forms::AccessManagementFormComponent.new(@storage, in_wizard: true))
respond_with_turbo_streams
end

respond_with_turbo_streams
end

def update
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,26 +64,14 @@ def create # rubocop:disable Metrics/AbcSize

service_result.on_failure do
update_via_turbo_stream(component: Storages::Admin::Forms::OAuthClientFormComponent.new(oauth_client: @oauth_client,
storage: @storage))
storage: @storage,
in_wizard: true))
respond_with_turbo_streams
end

service_result.on_success do
if @storage.provider_type_nextcloud?
prepare_storage_for_automatic_management_form
end

update_via_turbo_stream(component: Storages::Admin::OAuthClientInfoComponent.new(oauth_client: @oauth_client,
storage: @storage))
update_via_turbo_stream(component: Storages::Admin::Forms::RedirectUriFormComponent.new(
oauth_client: @oauth_client, storage: @storage, is_complete: false
))

if @storage.provider_type_nextcloud? && @storage.automatic_management_new_record?
update_via_turbo_stream(component: Storages::Admin::Forms::AutomaticallyManagedProjectFoldersFormComponent.new(@storage))
end
redirect_to(new_admin_settings_storage_path(continue_wizard: @storage.id), status: :see_other)
end

respond_with_turbo_streams
end

def update
Expand Down
Loading

0 comments on commit 4397711

Please sign in to comment.