diff --git a/Gemfile b/Gemfile
index 5ef6e016d27..485885b8cd1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -62,7 +62,8 @@ gem "strong_migrations", "~> 1.7"
gem "phlex-rails", "~> 1.1"
# Admin dashboard
-gem "avo", "~> 2.46"
+gem "avo", "~> 3.2"
+# gem "avo-pro", "~> 3.2", source: "https://packager.dev/avo-hq/"
gem "view_component", "~> 3.9"
gem "pundit", "~> 2.3"
gem "chartkick", "~> 5.0"
diff --git a/Gemfile.lock b/Gemfile.lock
index cf88fdaa76c..824781d7e73 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -84,10 +84,11 @@ GEM
attr_required (1.0.2)
autoprefixer-rails (10.4.16.0)
execjs (~> 2)
- avo (2.46.0)
- actionview (>= 6.0)
+ avo (3.2.3)
+ actionview (>= 6.1)
active_link_to
- activerecord (>= 6.0)
+ activerecord (>= 6.1)
+ activesupport (>= 6.1)
addressable
docile
dry-initializer
@@ -96,9 +97,9 @@ GEM
meta-tags
pagy
turbo-rails
- turbo_power (~> 0.5.0)
- view_component (>= 2.54.0)
- zeitwerk (>= 2.6.2)
+ turbo_power (>= 0.6.0)
+ view_component (>= 3.7.0)
+ zeitwerk (>= 2.6.12)
awrence (1.2.1)
aws-eventstream (1.3.0)
aws-partitions (1.873.0)
@@ -347,8 +348,8 @@ GEM
marcel (1.0.2)
matrix (0.4.2)
memory_profiler (1.0.1)
- meta-tags (2.19.0)
- actionpack (>= 3.2.0, < 7.2)
+ meta-tags (2.20.0)
+ actionpack (>= 6.0.0, < 7.2)
method_source (1.0.0)
mini_histogram (0.3.1)
mini_mime (1.1.5)
@@ -427,7 +428,7 @@ GEM
openssl-signature_algorithm (1.3.0)
openssl (> 2.0)
optimist (3.1.0)
- pagy (6.2.0)
+ pagy (6.3.0)
parallel (1.22.1)
parser (3.2.2.4)
ast (~> 2.4.1)
@@ -646,8 +647,8 @@ GEM
actionpack (>= 6.0.0)
activejob (>= 6.0.0)
railties (>= 6.0.0)
- turbo_power (0.5.0)
- turbo-rails (~> 1.3)
+ turbo_power (0.6.1)
+ turbo-rails (>= 1.3.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.4.2)
@@ -699,7 +700,7 @@ DEPENDENCIES
aggregate_assertions (~> 0.2.0)
amazing_print (~> 1.4)
autoprefixer-rails (~> 10.4)
- avo (~> 2.46)
+ avo (~> 3.2)
aws-sdk-s3 (~> 1.142)
aws-sdk-sqs (~> 1.69)
bcrypt (~> 3.1)
diff --git a/app/avo/actions/add_owner.rb b/app/avo/actions/add_owner.rb
index bd98d1b8993..b24cbd71f94 100644
--- a/app/avo/actions/add_owner.rb
+++ b/app/avo/actions/add_owner.rb
@@ -1,10 +1,12 @@
-class AddOwner < BaseAction
+class Avo::Actions::AddOwner < Avo::Actions::ApplicationAction
self.name = "Add owner"
self.visible = lambda {
current_user.team_member?("rubygems-org") && view == :show
}
- field :owner, as: :select_record, searchable: true, name: "New owner", use_resource: UserResource
+ def fields
+ field :owner, as: :select_record, searchable: true, name: "New owner", use_resource: Avo::Resources::User
+ end
self.message = lambda {
"Are you sure you would like to add an owner to #{record.name}?"
@@ -22,16 +24,16 @@ class ActionHandler < ActionHandler
error "Cannot add #{@owner.name} as an owner since they are unconfirmed" if @owner.unconfirmed?
end
- def do_handle_model(rubygem)
+ def do_handle_record(rubygem)
@rubygem = rubygem
super
end
- set_callback :handle_model, :before do
+ set_callback :handle_record, :before do
error "Cannot add #{@owner.name} as an owner since they are already an owner of #{@rubygem.name}" if @owner.rubygems.include?(@rubygem)
end
- def handle_model(rubygem)
+ def handle_record(rubygem)
authorizer = User.security_user
rubygem.ownerships.create!(user: @owner, authorizer: authorizer, confirmed_at: Time.current)
succeed "Added #{@owner.name} to #{@rubygem.name}"
diff --git a/app/avo/actions/base_action.rb b/app/avo/actions/application_action.rb
similarity index 68%
rename from app/avo/actions/base_action.rb
rename to app/avo/actions/application_action.rb
index 4e985aef5b2..43a0776dbc6 100644
--- a/app/avo/actions/base_action.rb
+++ b/app/avo/actions/application_action.rb
@@ -1,16 +1,18 @@
-class BaseAction < Avo::BaseAction
+class Avo::Actions::ApplicationAction < Avo::BaseAction
include SemanticLogger::Loggable
- field :comment, as: :textarea, required: true,
- help: "A comment explaining why this action was taken.
Will be saved in the audit log.
Must be more than 10 characters."
-
- def self.inherited(base)
- super
- base.items_holder = Avo::ItemsHolder.new
- base.items_holder.instance_variable_get(:@items).replace items_holder.instance_variable_get(:@items).deep_dup
- base.items_holder.invalid_fields.replace items_holder.invalid_fields.deep_dup
+ def fields
+ field :comment, as: :textarea, required: true,
+ help: "A comment explaining why this action was taken.
Will be saved in the audit log.
Must be more than 10 characters."
end
+ # def self.inherited(base)
+ # super
+ # base.items_holder = Avo::Resources::Items::Holder.new
+ # base.items_holder.instance_variable_get(:@items).replace items_holder.instance_variable_get(:@items).deep_dup
+ # base.items_holder.invalid_fields.replace items_holder.invalid_fields.deep_dup
+ # end
+
class ActionHandler
include Auditable
include SemanticLogger::Loggable
@@ -20,7 +22,7 @@ class ActionHandler
result_lambda.call
target.errored?
}
- define_callbacks :handle_model, terminator: lambda { |target, result_lambda|
+ define_callbacks :handle_record, terminator: lambda { |target, result_lambda|
result_lambda.call
target.errored?
}
@@ -35,9 +37,9 @@ def initialize( # rubocop:disable Metrics/ParameterLists
arguments:,
resource:,
action:,
- models: nil
+ records: nil
)
- @models = models
+ @records = records
@fields = fields
@current_user = current_user
@arguments = arguments
@@ -46,7 +48,7 @@ def initialize( # rubocop:disable Metrics/ParameterLists
@action = action
end
- attr_reader :models, :fields, :current_user, :arguments, :resource
+ attr_reader :records, :fields, :current_user, :arguments, :resource
delegate :error, :avo, :keep_modal_open, :redirect_to, :inform, :action_name, :succeed, :logger,
to: :@action
@@ -72,9 +74,9 @@ def do_handle
keep_modal_open if errored?
end
- def do_handle_model(model)
- run_callbacks :handle_model do
- handle_model(model)
+ def do_handle_record(record)
+ run_callbacks :handle_record do
+ handle_record(record)
end
end
@@ -89,7 +91,7 @@ def do_handle_standalone
action: action_name,
fields:,
arguments:,
- models:
+ records:
) do
run_callbacks :handle_standalone do
handle_standalone
@@ -99,17 +101,17 @@ def do_handle_standalone
end
def handle
- return do_handle_standalone if models.nil?
- models.each do |model|
+ return do_handle_standalone if records.nil?
+ records.each do |record|
_, audit = in_audited_transaction(
- auditable: model,
+ auditable: record,
admin_github_user: current_user,
action: action_name,
fields:,
arguments:,
- models:
+ records:
) do
- do_handle_model(model)
+ do_handle_record(record)
end
redirect_to avo.resources_audit_path(audit)
end
diff --git a/app/avo/actions/block_user.rb b/app/avo/actions/block_user.rb
index d73eb3fb7f8..6bde6c44fc0 100644
--- a/app/avo/actions/block_user.rb
+++ b/app/avo/actions/block_user.rb
@@ -1,4 +1,4 @@
-class BlockUser < BaseAction
+class Avo::Actions::BlockUser < Avo::Actions::ApplicationAction
self.name = "Block User"
self.visible = lambda {
current_user.team_member?("rubygems-org") && view == :show
@@ -11,7 +11,7 @@ class BlockUser < BaseAction
self.confirm_button_label = "Block User"
class ActionHandler < ActionHandler
- def handle_model(user)
+ def handle_record(user)
user.block!
end
end
diff --git a/app/avo/actions/change_user_email.rb b/app/avo/actions/change_user_email.rb
index 949a0f82feb..a24e5782957 100644
--- a/app/avo/actions/change_user_email.rb
+++ b/app/avo/actions/change_user_email.rb
@@ -1,5 +1,7 @@
-class ChangeUserEmail < BaseAction
- field :from_email, name: "Email", as: :text, required: true
+class Avo::Actions::ChangeUserEmail < Avo::Actions::ApplicationAction
+ def fields
+ field :from_email, name: "Email", as: :text, required: true
+ end
self.name = "Change User Email"
self.visible = lambda {
@@ -9,7 +11,7 @@ class ChangeUserEmail < BaseAction
self.confirm_button_label = "Change User Email"
class ActionHandler < ActionHandler
- def handle_model(user)
+ def handle_record(user)
user.email = fields["from_email"]
user.email_confirmed = false
user.generate_confirmation_token
diff --git a/app/avo/actions/create_user.rb b/app/avo/actions/create_user.rb
index 1c990b018c5..752ab0154b7 100644
--- a/app/avo/actions/create_user.rb
+++ b/app/avo/actions/create_user.rb
@@ -1,6 +1,4 @@
-class CreateUser < BaseAction
- field :email, name: "Email", as: :text, required: true
-
+class Avo::Actions::CreateUser < Avo::Actions::ApplicationAction
self.name = "Create User"
self.visible = lambda {
current_user.team_member?("rubygems-org") && view == :index && !Rails.env.production?
@@ -9,6 +7,10 @@ class CreateUser < BaseAction
self.confirm_button_label = "Create User"
+ def fields
+ field :email, name: "Email", as: :text, required: true
+ end
+
class ActionHandler < ActionHandler
def handle_standalone
user = User.new(
diff --git a/app/avo/actions/delete_webhook.rb b/app/avo/actions/delete_webhook.rb
index 4a696fbc5c8..0aa4ba4d5d3 100644
--- a/app/avo/actions/delete_webhook.rb
+++ b/app/avo/actions/delete_webhook.rb
@@ -1,4 +1,4 @@
-class DeleteWebhook < BaseAction
+class Avo::Actions::DeleteWebhook < Avo::Actions::ApplicationAction
self.name = "Delete Webhook"
self.visible = lambda {
current_user.team_member?("rubygems-org") && view == :show
@@ -11,7 +11,7 @@ class DeleteWebhook < BaseAction
self.confirm_button_label = "Delete Webhook"
class ActionHandler < ActionHandler
- def handle_model(webhook)
+ def handle_record(webhook)
webhook.destroy!
WebHooksMailer.webhook_deleted(webhook.user_id, webhook.rubygem_id, webhook.url, webhook.failure_count).deliver_later
end
diff --git a/app/avo/actions/refresh_oidc_provider.rb b/app/avo/actions/refresh_oidc_provider.rb
index c53e53d766f..a1f025d9be2 100644
--- a/app/avo/actions/refresh_oidc_provider.rb
+++ b/app/avo/actions/refresh_oidc_provider.rb
@@ -1,4 +1,4 @@
-class RefreshOIDCProvider < BaseAction
+class Avo::Actions::RefreshOIDCProvider < Avo::Actions::ApplicationAction
self.name = "Refresh OIDC Provider"
self.visible = lambda {
current_user.team_member?("rubygems-org") && view == :show
@@ -11,7 +11,7 @@ class RefreshOIDCProvider < BaseAction
self.confirm_button_label = "Refresh"
class ActionHandler < ActionHandler
- def handle_model(provider)
+ def handle_record(provider)
RefreshOIDCProviderJob.perform_now(provider:)
end
end
diff --git a/app/avo/actions/release_reserved_namespace.rb b/app/avo/actions/release_reserved_namespace.rb
index 1cbbe8b793c..0dcad48a02a 100644
--- a/app/avo/actions/release_reserved_namespace.rb
+++ b/app/avo/actions/release_reserved_namespace.rb
@@ -1,4 +1,4 @@
-class ReleaseReservedNamespace < BaseAction
+class Avo::Actions::ReleaseReservedNamespace < Avo::Actions::ApplicationAction
self.name = "Release reserved namespace"
self.visible = lambda {
current_user.team_member?("rubygems-org") && view == :show
@@ -11,7 +11,7 @@ class ReleaseReservedNamespace < BaseAction
self.confirm_button_label = "Release namespace"
class ActionHandler < ActionHandler
- def handle_model(rubygem)
+ def handle_record(rubygem)
rubygem.release_reserved_namespace!
end
end
diff --git a/app/avo/actions/reset_api_key.rb b/app/avo/actions/reset_api_key.rb
index 9f5240c4958..a2a1a8f50bc 100644
--- a/app/avo/actions/reset_api_key.rb
+++ b/app/avo/actions/reset_api_key.rb
@@ -1,4 +1,4 @@
-class ResetApiKey < BaseAction
+class Avo::Actions::ResetApiKey < Avo::Actions::ApplicationAction
self.name = "Reset Api Key"
self.visible = lambda {
current_user.team_member?("rubygems-org") && view == :show
@@ -8,15 +8,17 @@ class ResetApiKey < BaseAction
}
self.confirm_button_label = "Reset Api Key"
- field :template, as: :select,
- options: {
- "Public Gem": :public_gem_reset_api_key,
- Honeycomb: :honeycomb_reset_api_key
- },
- help: "Select mailer template"
+ def fields
+ field :template, as: :select,
+ options: {
+ "Public Gem": :public_gem_reset_api_key,
+ Honeycomb: :honeycomb_reset_api_key
+ },
+ help: "Select mailer template"
+ end
class ActionHandler < ActionHandler
- def handle_model(user)
+ def handle_record(user)
user.reset_api_key!
Mailer.reset_api_key(user, fields["template"]).deliver_later
diff --git a/app/avo/actions/reset_user_2fa.rb b/app/avo/actions/reset_user_2fa.rb
index 03e19a1d518..dc6d2f83f77 100644
--- a/app/avo/actions/reset_user_2fa.rb
+++ b/app/avo/actions/reset_user_2fa.rb
@@ -1,4 +1,4 @@
-class ResetUser2fa < BaseAction
+class Avo::Actions::ResetUser2fa < Avo::Actions::ApplicationAction
self.name = "Reset User 2FA"
self.visible = lambda {
current_user.team_member?("rubygems-org") && view == :show
@@ -11,7 +11,7 @@ class ResetUser2fa < BaseAction
self.confirm_button_label = "Reset MFA"
class ActionHandler < ActionHandler
- def handle_model(user)
+ def handle_record(user)
user.disable_totp!
user.password = SecureRandom.hex(20).encode("UTF-8")
user.save!
diff --git a/app/avo/actions/restore_version.rb b/app/avo/actions/restore_version.rb
index 2a5bf7f250f..15b9f816984 100644
--- a/app/avo/actions/restore_version.rb
+++ b/app/avo/actions/restore_version.rb
@@ -1,9 +1,9 @@
-class RestoreVersion < BaseAction
+class Avo::Actions::RestoreVersion < Avo::Actions::ApplicationAction
self.name = "Restore version"
self.visible = lambda {
current_user.team_member?("rubygems-org") &&
view == :show &&
- resource.model.deletion.present?
+ resource.record.deletion.present?
}
self.message = lambda {
"Are you sure you would like to restore #{record.slug} with "
@@ -11,7 +11,7 @@ class RestoreVersion < BaseAction
self.confirm_button_label = "Restore version"
class ActionHandler < ActionHandler
- def handle_model(version)
+ def handle_record(version)
version.deletion&.restore!
end
end
diff --git a/app/avo/actions/upload_info_file.rb b/app/avo/actions/upload_info_file.rb
index 38740af0a4f..2e9bfb6191f 100644
--- a/app/avo/actions/upload_info_file.rb
+++ b/app/avo/actions/upload_info_file.rb
@@ -1,4 +1,4 @@
-class UploadInfoFile < BaseAction
+class Avo::Actions::UploadInfoFile < Avo::Actions::ApplicationAction
self.name = "Upload Info File"
self.visible = lambda {
current_user.team_member?("rubygems-org") && view == :show
@@ -6,7 +6,7 @@ class UploadInfoFile < BaseAction
self.confirm_button_label = "Upload"
class ActionHandler < ActionHandler
- def handle_model(rubygem)
+ def handle_record(rubygem)
UploadInfoFileJob.perform_later(rubygem_name: rubygem.name)
succeed("Upload job scheduled")
diff --git a/app/avo/actions/upload_names_file.rb b/app/avo/actions/upload_names_file.rb
index 1ab53b4ce95..52ca1fb383d 100644
--- a/app/avo/actions/upload_names_file.rb
+++ b/app/avo/actions/upload_names_file.rb
@@ -1,4 +1,4 @@
-class UploadNamesFile < BaseAction
+class Avo::Actions::UploadNamesFile < Avo::Actions::ApplicationAction
self.name = "Upload Names File"
self.visible = lambda {
current_user.team_member?("rubygems-org") && view == :index
diff --git a/app/avo/actions/upload_versions_file.rb b/app/avo/actions/upload_versions_file.rb
index ee5f700a02a..a051aafc48c 100644
--- a/app/avo/actions/upload_versions_file.rb
+++ b/app/avo/actions/upload_versions_file.rb
@@ -1,4 +1,4 @@
-class UploadVersionsFile < BaseAction
+class Avo::Actions::UploadVersionsFile < Avo::Actions::ApplicationAction
self.name = "Upload Versions File"
self.visible = lambda {
current_user.team_member?("rubygems-org") && view == :index
diff --git a/app/avo/actions/yank_rubygem.rb b/app/avo/actions/yank_rubygem.rb
index 13de715d4d7..24828bc00fe 100644
--- a/app/avo/actions/yank_rubygem.rb
+++ b/app/avo/actions/yank_rubygem.rb
@@ -1,17 +1,19 @@
-class YankRubygem < BaseAction
+class Avo::Actions::YankRubygem < Avo::Actions::ApplicationAction
OPTION_ALL = "All".freeze
- field :version, as: :select,
- options: lambda { |model:, resource:, view:, field:| # rubocop:disable Lint/UnusedBlockArgument
- [OPTION_ALL] + model.versions.indexed.pluck(:number, :id)
- },
- help: "Select Version which needs to be yanked."
+ def fields
+ field :version, as: :select,
+ options: lambda { |record:, resource:, view:, field:| # rubocop:disable Lint/UnusedBlockArgument
+ [OPTION_ALL] + record.versions.indexed.pluck(:number, :id)
+ },
+ help: "Select Version which needs to be yanked."
+ end
self.name = "Yank Rubygem"
self.visible = lambda {
current_user.team_member?("rubygems-org") &&
view == :show &&
- resource.model.versions.indexed.present?
+ resource.record.versions.indexed.present?
}
self.message = lambda {
@@ -21,7 +23,7 @@ class YankRubygem < BaseAction
self.confirm_button_label = "Yank Rubygem"
class ActionHandler < ActionHandler
- def handle_model(rubygem)
+ def handle_record(rubygem)
version_id = fields["version"]
version_id_to_yank = version_id if version_id != OPTION_ALL
diff --git a/app/avo/actions/yank_rubygems_for_user.rb b/app/avo/actions/yank_rubygems_for_user.rb
index 3d88769b0cc..7ce5ca1564a 100644
--- a/app/avo/actions/yank_rubygems_for_user.rb
+++ b/app/avo/actions/yank_rubygems_for_user.rb
@@ -1,9 +1,9 @@
-class YankRubygemsForUser < BaseAction
+class Avo::Actions::YankRubygemsForUser < Avo::Actions::ApplicationAction
self.name = "Yank all Rubygems"
self.visible = lambda {
current_user.team_member?("rubygems-org") &&
view == :show &&
- resource.model.rubygems.present?
+ resource.record.rubygems.present?
}
self.message = lambda {
@@ -13,7 +13,7 @@ class YankRubygemsForUser < BaseAction
self.confirm_button_label = "Yank all Rubygems"
class ActionHandler < ActionHandler
- def handle_model(user)
+ def handle_record(user)
user.rubygems.find_each(&:yank_versions!)
end
end
diff --git a/app/avo/actions/yank_user.rb b/app/avo/actions/yank_user.rb
index 81e6dc41106..14dc9735eef 100644
--- a/app/avo/actions/yank_user.rb
+++ b/app/avo/actions/yank_user.rb
@@ -1,4 +1,4 @@
-class YankUser < BaseAction
+class Avo::Actions::YankUser < Avo::Actions::ApplicationAction
self.name = "Yank User"
self.visible = lambda {
current_user.team_member?("rubygems-org") && view == :show
@@ -9,7 +9,7 @@ class YankUser < BaseAction
self.confirm_button_label = "Yank User"
class ActionHandler < ActionHandler
- def handle_model(user)
+ def handle_record(user)
rubygems = user.rubygems
rubygems.find_each(&:yank_versions!)
diff --git a/app/avo/cards/dashboard_welcome_card.rb b/app/avo/cards/dashboard_welcome_card.rb
index 8cbadb73c47..e5d37e51490 100644
--- a/app/avo/cards/dashboard_welcome_card.rb
+++ b/app/avo/cards/dashboard_welcome_card.rb
@@ -1,4 +1,4 @@
-class DashboardWelcomeCard < Avo::Dashboards::PartialCard
+class Avo::Cards::DashboardWelcomeCard < Avo::Cards::PartialCard
self.id = "dashboard_welcome_card"
self.label = "Welcome to the RubyGems.org admin dashboard!"
self.partial = "avo/cards/dashboard_welcome_card"
diff --git a/app/avo/cards/pushes_chart.rb b/app/avo/cards/pushes_chart.rb
index 5d45743df0b..523970c762f 100644
--- a/app/avo/cards/pushes_chart.rb
+++ b/app/avo/cards/pushes_chart.rb
@@ -1,4 +1,4 @@
-class PushesChart < Avo::Dashboards::ChartkickCard
+class Avo::Cards::PushesChart < Avo::Cards::ChartkickCard
self.id = "pushes_chart"
self.label = "Pushes by day"
self.chart_type = :line_chart
diff --git a/app/avo/cards/rubygems_metric.rb b/app/avo/cards/rubygems_metric.rb
index 03f9dfc57a6..cd74a871bed 100644
--- a/app/avo/cards/rubygems_metric.rb
+++ b/app/avo/cards/rubygems_metric.rb
@@ -1,4 +1,4 @@
-class RubygemsMetric < Avo::Dashboards::MetricCard
+class Avo::Cards::RubygemsMetric < Avo::Cards::MetricCard
self.id = "rubygems_metric"
self.label = "RubyGems "
self.cols = 2
diff --git a/app/avo/cards/users_metric.rb b/app/avo/cards/users_metric.rb
index 18033d49463..e9eb71941f2 100644
--- a/app/avo/cards/users_metric.rb
+++ b/app/avo/cards/users_metric.rb
@@ -1,4 +1,4 @@
-class UsersMetric < Avo::Dashboards::MetricCard
+class Avo::Cards::UsersMetric < Avo::Cards::MetricCard
self.id = "users_metric"
self.label = "Total users"
self.cols = 2
diff --git a/app/avo/cards/versions_metric.rb b/app/avo/cards/versions_metric.rb
index fefa912a30f..0c245831f40 100644
--- a/app/avo/cards/versions_metric.rb
+++ b/app/avo/cards/versions_metric.rb
@@ -1,4 +1,4 @@
-class VersionsMetric < Avo::Dashboards::MetricCard
+class Avo::Cards::VersionsMetric < Avo::Cards::MetricCard
self.id = "versions_metric"
self.label = "Versions pushed"
diff --git a/app/avo/dashboards/dashy.rb b/app/avo/dashboards/dashy.rb
index 3a5ef54376c..08214f75084 100644
--- a/app/avo/dashboards/dashy.rb
+++ b/app/avo/dashboards/dashy.rb
@@ -1,21 +1,21 @@
-class Dashy < Avo::Dashboards::BaseDashboard
+class Avo::Dashboards::Dashy < Avo::Dashboards::BaseDashboard
self.id = "dashy"
- self.name = "Dashy"
+ self.name = "Avo::Dashboards::Dashy"
self.grid_cols = 6
self.visible = lambda {
current_user.team_member?("rubygems-org")
}
# cards go here
- card DashboardWelcomeCard
+ card Avo::Cards::DashboardWelcomeCard
divider label: "Metrics"
- card UsersMetric
- card VersionsMetric
- card RubygemsMetric
+ card Avo::Cards::UsersMetric
+ card Avo::Cards::VersionsMetric
+ card Avo::Cards::RubygemsMetric
divider label: "Charts"
- card PushesChart
+ card Avo::Cards::PushesChart
end
diff --git a/app/avo/fields/array_of_field.rb b/app/avo/fields/array_of_field.rb
index e798c45244b..962403db029 100644
--- a/app/avo/fields/array_of_field.rb
+++ b/app/avo/fields/array_of_field.rb
@@ -1,9 +1,9 @@
-class ArrayOfField < Avo::Fields::BaseField
+class Avo::Fields::ArrayOfField < Avo::Fields::BaseField
def initialize(name, field:, field_options: {}, **args, &block)
super(name, **args, &nil)
@make_field = lambda do |id:, index: nil, value: nil|
- items_holder = Avo::ItemsHolder.new
+ items_holder = Avo::Resources::Items::Holder.new
items_holder.field(id, name: index&.to_s || self.name, as: field, required: -> { false }, value:, **field_options, &block)
items_holder.items.sole.hydrate(view:, resource:)
end
diff --git a/app/avo/fields/audited_changes_field.rb b/app/avo/fields/audited_changes_field.rb
index 4d4398abcb3..3f4e8b522bb 100644
--- a/app/avo/fields/audited_changes_field.rb
+++ b/app/avo/fields/audited_changes_field.rb
@@ -1,2 +1,2 @@
-class AuditedChangesField < Avo::Fields::BaseField
+class Avo::Fields::AuditedChangesField < Avo::Fields::BaseField
end
diff --git a/app/avo/fields/json_viewer_field.rb b/app/avo/fields/json_viewer_field.rb
index df4d52e45d3..aedb7030e5f 100644
--- a/app/avo/fields/json_viewer_field.rb
+++ b/app/avo/fields/json_viewer_field.rb
@@ -1,4 +1,4 @@
-class JsonViewerField < Avo::Fields::CodeField
+class Avo::Fields::JsonViewerField < Avo::Fields::CodeField
def initialize(name, **args, &)
super(name, **args, language: :javascript, line_wrapping: true, &)
end
diff --git a/app/avo/fields/nested_field.rb b/app/avo/fields/nested_field.rb
index b621fa3eb02..0b2cdc70f61 100644
--- a/app/avo/fields/nested_field.rb
+++ b/app/avo/fields/nested_field.rb
@@ -1,8 +1,8 @@
-class NestedField < Avo::Fields::BaseField
+class Avo::Fields::NestedField < Avo::Fields::BaseField
include Avo::Concerns::HasFields
def initialize(name, stacked: true, **args, &block)
- @items_holder = Avo::ItemsHolder.new
+ @items_holder = Avo::Resources::Items::Holder.new
hide_on [:index]
super(name, stacked:, **args, &nil)
instance_exec(&block) if block
diff --git a/app/avo/fields/select_record_field.rb b/app/avo/fields/select_record_field.rb
index 83da8985919..b5b2a25a45e 100644
--- a/app/avo/fields/select_record_field.rb
+++ b/app/avo/fields/select_record_field.rb
@@ -1,4 +1,4 @@
-class SelectRecordField < Avo::Fields::BelongsToField
+class Avo::Fields::SelectRecordField < Avo::Fields::BelongsToField
def foreign_key
id
end
diff --git a/app/avo/filters/email_filter.rb b/app/avo/filters/email_filter.rb
index 2a3375c86c5..2cbc9e19912 100644
--- a/app/avo/filters/email_filter.rb
+++ b/app/avo/filters/email_filter.rb
@@ -1,4 +1,4 @@
-class EmailFilter < Avo::Filters::TextFilter
+class Avo::Filters::EmailFilter < Avo::Filters::TextFilter
self.name = "Email filter"
self.button_label = "Filter by email"
diff --git a/app/avo/filters/scope_boolean_filter.rb b/app/avo/filters/scope_boolean_filter.rb
index 6addbbcb400..67543347576 100644
--- a/app/avo/filters/scope_boolean_filter.rb
+++ b/app/avo/filters/scope_boolean_filter.rb
@@ -1,4 +1,4 @@
-class ScopeBooleanFilter < Avo::Filters::BooleanFilter
+class Avo::Filters::ScopeBooleanFilter < Avo::Filters::BooleanFilter
def name
arguments.fetch(:name) { self.class.to_s.demodulize.underscore.sub(/_filter$/, "").titleize }
end
diff --git a/app/avo/resources/admin_github_user.rb b/app/avo/resources/admin_github_user.rb
new file mode 100644
index 00000000000..40a2a6febb6
--- /dev/null
+++ b/app/avo/resources/admin_github_user.rb
@@ -0,0 +1,34 @@
+class Avo::Resources::AdminGitHubUser < Avo::BaseResource
+ self.title = :login
+ self.includes = []
+ self.model_class = ::Admin::GitHubUser
+ if Gem.loaded_specs["avo-pro"]
+ self.search_query = lambda {
+ query.where("login LIKE ?", "%#{params[:q]}%")
+ }
+ end
+
+ self.description = "GitHub users that have authenticated via the admin OAuth flow."
+
+ def fields
+ field :id, as: :id
+
+ field :is_admin, as: :boolean, readonly: true
+ field :login, as: :text, readonly: true,
+ as_html: true,
+ format_using: -> { link_to value, "https://github.com/#{value}" }
+ field :avatar_url, as: :external_image, name: "Avatar", readonly: true
+ field :github_id, as: :text, readonly: true
+ field :oauth_token, as: :text, visible: -> { false }
+
+ field :details, as: :heading
+
+ field :teams, as: :tags, readonly: true, format_using: -> { value.pluck(:slug) }
+
+ field :info_data,
+ as: :code, readonly: true, language: :javascript,
+ format_using: -> { JSON.pretty_generate value }
+
+ field :audits, as: :has_many
+ end
+end
diff --git a/app/avo/resources/admin_github_user_resource.rb b/app/avo/resources/admin_github_user_resource.rb
deleted file mode 100644
index d9a915f1af5..00000000000
--- a/app/avo/resources/admin_github_user_resource.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-class AdminGitHubUserResource < Avo::BaseResource
- self.title = :login
- self.includes = []
- self.model_class = ::Admin::GitHubUser
- self.search_query = lambda {
- scope.where("login LIKE ?", "%#{params[:q]}%")
- }
-
- self.description = "GitHub users that have authenticated via the admin OAuth flow."
-
- field :id, as: :id
-
- field :is_admin, as: :boolean, readonly: true
- field :login, as: :text, readonly: true,
- as_html: true,
- format_using: -> { link_to value, "https://github.com/#{value}" }
- field :avatar_url, as: :external_image, name: "Avatar", readonly: true
- field :github_id, as: :text, readonly: true
- field :oauth_token, as: :text, visible: ->(resource:) { false } # rubocop:disable Lint/UnusedBlockArgument
-
- heading "Details"
-
- field :teams, as: :tags, readonly: true, format_using: -> { value.pluck(:slug) }
-
- field :info_data,
- as: :code, readonly: true, language: :javascript,
- format_using: -> { JSON.pretty_generate value }
-
- field :audits, as: :has_many
-end
diff --git a/app/avo/resources/api_key.rb b/app/avo/resources/api_key.rb
new file mode 100644
index 00000000000..c3c2251565e
--- /dev/null
+++ b/app/avo/resources/api_key.rb
@@ -0,0 +1,46 @@
+class Avo::Resources::ApiKey < Avo::BaseResource
+ self.title = :name
+ self.includes = []
+
+ class ExpiredFilter < Avo::Filters::ScopeBooleanFilter; end
+
+ def filters
+ filter ExpiredFilter, arguments: { default: { expired: false, unexpired: true } }
+ end
+
+ def fields
+ main_panel do
+ field :id, as: :id, hide_on: :index
+
+ field :name, as: :text, link_to_resource: true
+ field :hashed_key, as: :text, visible: -> { false }
+ field :user, as: :belongs_to, visible: -> { false }
+ field :owner, as: :belongs_to,
+ polymorphic_as: :owner,
+ types: [::User, ::OIDC::TrustedPublisher::GitHubAction]
+ field :last_accessed_at, as: :date_time
+ field :soft_deleted_at, as: :date_time
+ field :soft_deleted_rubygem_name, as: :text
+ field :expires_at, as: :date_time
+
+ field :enabled_scopes, as: :tags
+
+ sidebar do
+ field :permissions, as: :heading
+
+ field :index_rubygems, as: :boolean
+ field :push_rubygem, as: :boolean
+ field :yank_rubygem, as: :boolean
+ field :add_owner, as: :boolean
+ field :remove_owner, as: :boolean
+ field :access_webhooks, as: :boolean
+ field :show_dashboard, as: :boolean
+ field :mfa, as: :boolean
+ end
+ end
+
+ field :api_key_rubygem_scope, as: :has_one
+ field :ownership, as: :has_one
+ field :oidc_id_token, as: :has_one
+ end
+end
diff --git a/app/avo/resources/api_key_resource.rb b/app/avo/resources/api_key_resource.rb
deleted file mode 100644
index 70832a69514..00000000000
--- a/app/avo/resources/api_key_resource.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-class ApiKeyResource < Avo::BaseResource
- self.title = :name
- self.includes = []
-
- class ExpiredFilter < ScopeBooleanFilter; end
- filter ExpiredFilter, arguments: { default: { expired: false, unexpired: true } }
-
- field :id, as: :id, hide_on: :index
-
- field :name, as: :text, link_to_resource: true
- field :hashed_key, as: :text, visible: ->(_) { false }
- field :user, as: :belongs_to, visible: ->(_) { false }
- field :owner, as: :belongs_to,
- polymorphic_as: :owner,
- types: [::User, ::OIDC::TrustedPublisher::GitHubAction]
- field :last_accessed_at, as: :date_time
- field :soft_deleted_at, as: :date_time
- field :soft_deleted_rubygem_name, as: :text
- field :expires_at, as: :date_time
-
- field :enabled_scopes, as: :tags
-
- sidebar do
- heading "Permissions"
-
- field :index_rubygems, as: :boolean
- field :push_rubygem, as: :boolean
- field :yank_rubygem, as: :boolean
- field :add_owner, as: :boolean
- field :remove_owner, as: :boolean
- field :access_webhooks, as: :boolean
- field :show_dashboard, as: :boolean
- field :mfa, as: :boolean
- end
-
- field :api_key_rubygem_scope, as: :has_one
- field :ownership, as: :has_one
- field :oidc_id_token, as: :has_one
-end
diff --git a/app/avo/resources/api_key_rubygem_scope.rb b/app/avo/resources/api_key_rubygem_scope.rb
new file mode 100644
index 00000000000..e8105285828
--- /dev/null
+++ b/app/avo/resources/api_key_rubygem_scope.rb
@@ -0,0 +1,11 @@
+class Avo::Resources::ApiKeyRubygemScope < Avo::BaseResource
+ self.title = :cache_key
+ self.includes = []
+
+ def fields
+ field :id, as: :id
+
+ field :api_key, as: :belongs_to
+ field :ownership, as: :belongs_to
+ end
+end
diff --git a/app/avo/resources/api_key_rubygem_scope_resource.rb b/app/avo/resources/api_key_rubygem_scope_resource.rb
deleted file mode 100644
index 9560edba16c..00000000000
--- a/app/avo/resources/api_key_rubygem_scope_resource.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class ApiKeyRubygemScopeResource < Avo::BaseResource
- self.title = :cache_key
- self.includes = []
-
- field :id, as: :id
-
- field :api_key, as: :belongs_to
- field :ownership, as: :belongs_to
-end
diff --git a/app/avo/resources/audit.rb b/app/avo/resources/audit.rb
new file mode 100644
index 00000000000..f674b6b7785
--- /dev/null
+++ b/app/avo/resources/audit.rb
@@ -0,0 +1,40 @@
+class Avo::Resources::Audit < Avo::BaseResource
+ self.title = :id
+ self.includes = %i[
+ admin_github_user
+ auditable
+ ]
+
+ def fields
+ field :action, as: :text
+
+ panel do
+ sidebar do
+ field :admin_github_user, as: :belongs_to
+ field :created_at, as: :date_time
+ field :comment, as: :text
+
+ field :auditable, as: :belongs_to,
+ polymorphic_as: :auditable,
+ types: [::User, ::WebHook],
+ name: "Edited Record"
+
+ field :action_details, as: :heading
+
+ field :audited_changes_arguments, as: :json_viewer, only_on: :show do |_model|
+ record.audited_changes["arguments"]
+ end
+ field :audited_changes_fields, as: :json_viewer, only_on: :show do |_model|
+ record.audited_changes["fields"]
+ end
+ field :audited_changes_models, as: :text, as_html: true, only_on: :show do
+ record.audited_changes["models"]
+ end
+
+ field :id, as: :id
+ end
+ end
+
+ field :audited_changes, as: :audited_changes, except_on: :index
+ end
+end
diff --git a/app/avo/resources/audit_resource.rb b/app/avo/resources/audit_resource.rb
deleted file mode 100644
index 1fc1af10f79..00000000000
--- a/app/avo/resources/audit_resource.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-class AuditResource < Avo::BaseResource
- self.title = :id
- self.includes = %i[
- admin_github_user
- auditable
- ]
-
- field :action, as: :text
-
- sidebar do
- field :admin_github_user, as: :belongs_to
- field :created_at, as: :date_time
- field :comment, as: :text
-
- field :auditable, as: :belongs_to,
- polymorphic_as: :auditable,
- types: [::User, ::WebHook],
- name: "Edited Record"
-
- heading "Action Details"
-
- field :audited_changes_arguments, as: :json_viewer, only_on: :show do |model|
- model.audited_changes["arguments"]
- end
- field :audited_changes_fields, as: :json_viewer, only_on: :show do |model|
- model.audited_changes["fields"]
- end
- field :audited_changes_models, as: :text, as_html: true, only_on: :show do
- model.audited_changes["models"]
- end
-
- field :id, as: :id
- end
-
- field :audited_changes, as: :audited_changes, except_on: :index
-end
diff --git a/app/avo/resources/concerns/avo_auditable_resource.rb b/app/avo/resources/concerns/avo_auditable_resource.rb
index 5eb219fb67b..2c4ae137983 100644
--- a/app/avo/resources/concerns/avo_auditable_resource.rb
+++ b/app/avo/resources/concerns/avo_auditable_resource.rb
@@ -1,20 +1,24 @@
-module Concerns::AvoAuditableResource
+module Avo::Resources::Concerns::AvoAuditableResource
extend ActiveSupport::Concern
class_methods do
- def inherited(base)
- super
- base.items_holder = Avo::ItemsHolder.new
- base.items_holder.instance_variable_get(:@items).replace items_holder.instance_variable_get(:@items).deep_dup
- base.items_holder.invalid_fields.replace items_holder.invalid_fields.deep_dup
+ if false
+ def inherited(base)
+ super
+ base.items_holder = Avo::Resources::Items::Holder.new
+ base.items_holder.instance_variable_get(:@items).replace items_holder.instance_variable_get(:@items).deep_dup
+ base.items_holder.invalid_fields.replace items_holder.invalid_fields.deep_dup
+ end
end
end
included do
- panel "Auditable" do
- field :comment, as: :textarea, required: true,
- help: "A comment explaining why this action was taken.
Will be saved in the audit log.
Must be more than 10 characters.",
- only_on: %i[new edit]
+ if false
+ panel "Auditable" do
+ field :comment, as: :textarea, required: true,
+ help: "A comment explaining why this action was taken.
Will be saved in the audit log.
Must be more than 10 characters.",
+ only_on: %i[new edit]
+ end
end
end
end
diff --git a/app/avo/resources/deletion.rb b/app/avo/resources/deletion.rb
new file mode 100644
index 00000000000..a52385d8afa
--- /dev/null
+++ b/app/avo/resources/deletion.rb
@@ -0,0 +1,15 @@
+class Avo::Resources::Deletion < Avo::BaseResource
+ self.title = :id
+ self.includes = [:version]
+
+ def fields
+ field :id, as: :id
+
+ field :created_at, as: :date_time, sortable: true, title: "Deleted At"
+ field :rubygem, as: :text
+ field :number, as: :text
+ field :platform, as: :text
+ field :user, as: :belongs_to
+ field :version, as: :belongs_to
+ end
+end
diff --git a/app/avo/resources/deletion_resource.rb b/app/avo/resources/deletion_resource.rb
deleted file mode 100644
index fdf0cca5387..00000000000
--- a/app/avo/resources/deletion_resource.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-class DeletionResource < Avo::BaseResource
- self.title = :id
- self.includes = [:version]
- # self.search_query = -> do
- # scope.ransack(id_eq: params[:q], m: "or").result(distinct: false)
- # end
-
- field :id, as: :id
- # Fields generated from the model
- field :created_at, as: :date_time, sortable: true, title: "Deleted At"
- field :rubygem, as: :text
- field :number, as: :text
- field :platform, as: :text
- field :user, as: :belongs_to
- field :version, as: :belongs_to
- # add fields here
-end
diff --git a/app/avo/resources/dependency.rb b/app/avo/resources/dependency.rb
new file mode 100644
index 00000000000..1e7d213ed68
--- /dev/null
+++ b/app/avo/resources/dependency.rb
@@ -0,0 +1,18 @@
+class Avo::Resources::Dependency < Avo::BaseResource
+ self.title = :id
+ self.includes = []
+
+ def fields
+ field :id, as: :id, link_to_resource: true
+
+ field :version, as: :belongs_to
+ field :rubygem, as: :belongs_to
+ field :requirements, as: :text
+ field :unresolved_name, as: :text
+
+ field :scope, as: :badge,
+ options: {
+ warning: "development"
+ }
+ end
+end
diff --git a/app/avo/resources/dependency_resource.rb b/app/avo/resources/dependency_resource.rb
deleted file mode 100644
index a3751556d8f..00000000000
--- a/app/avo/resources/dependency_resource.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-class DependencyResource < Avo::BaseResource
- self.title = :id
- self.includes = []
- # self.search_query = -> do
- # scope.ransack(id_eq: params[:q], m: "or").result(distinct: false)
- # end
-
- field :id, as: :id, link_to_resource: true
-
- field :version, as: :belongs_to
- field :rubygem, as: :belongs_to
- field :requirements, as: :text
- field :unresolved_name, as: :text
-
- field :scope, as: :badge,
- options: {
- warning: "development"
- }
-end
diff --git a/app/avo/resources/gem_download.rb b/app/avo/resources/gem_download.rb
new file mode 100644
index 00000000000..40c23591359
--- /dev/null
+++ b/app/avo/resources/gem_download.rb
@@ -0,0 +1,32 @@
+class Avo::Resources::GemDownload < Avo::BaseResource
+ self.title = :inspect
+ self.includes = %i[rubygem version]
+
+ self.index_query = lambda {
+ query.order(count: :desc)
+ }
+
+ class SpecificityFilter < Avo::Filters::ScopeBooleanFilter; end
+
+ def filters
+ filter SpecificityFilter, arguments: { default: { for_versions: true, for_rubygems: true, total: true } }
+ end
+
+ def fields
+ field :title, as: :text, link_to_resource: true do |_model, _resource, _view|
+ if record.version
+ "#{record.version.full_name} (#{record.count.to_fs(:delimited)})"
+ elsif record.rubygem
+ "#{record.rubygem} (#{record.count.to_fs(:delimited)})"
+ else
+ "All Gems (#{record.count.to_fs(:delimited)})"
+ end
+ end
+
+ field :rubygem, as: :belongs_to
+ field :version, as: :belongs_to
+ field :count, as: :number, sortable: true, index_text_align: :right, format_using: -> { value.to_fs(:delimited) }, default: 0
+
+ field :id, as: :id, hide_on: :index
+ end
+end
diff --git a/app/avo/resources/gem_download_resource.rb b/app/avo/resources/gem_download_resource.rb
deleted file mode 100644
index 4cc08993f07..00000000000
--- a/app/avo/resources/gem_download_resource.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-class GemDownloadResource < Avo::BaseResource
- self.title = :inspect
- self.includes = %i[rubygem version]
-
- self.resolve_query_scope = lambda { |model_class:|
- model_class.order(count: :desc)
- }
-
- class SpecificityFilter < ScopeBooleanFilter; end
- filter SpecificityFilter, arguments: { default: { for_versions: true, for_rubygems: true, total: true } }
-
- field :title, as: :text, link_to_resource: true do |model, _resource, _view|
- if model.version
- "#{model.version.full_name} (#{model.count.to_fs(:delimited)})"
- elsif model.rubygem
- "#{model.rubygem} (#{model.count.to_fs(:delimited)})"
- else
- "All Gems (#{model.count.to_fs(:delimited)})"
- end
- end
-
- field :rubygem, as: :belongs_to
- field :version, as: :belongs_to
- field :count, as: :number, sortable: true, index_text_align: :right, format_using: -> { value.to_fs(:delimited) }, default: 0
-
- field :id, as: :id, hide_on: :index
-end
diff --git a/app/avo/resources/gem_name_reservation.rb b/app/avo/resources/gem_name_reservation.rb
new file mode 100644
index 00000000000..74ff7d3c43d
--- /dev/null
+++ b/app/avo/resources/gem_name_reservation.rb
@@ -0,0 +1,14 @@
+class Avo::Resources::GemNameReservation < Avo::BaseResource
+ self.title = :name
+ self.includes = []
+ if Gem.loaded_specs["avo-pro"]
+ self.search_query = lambda {
+ query.where("name LIKE ?", "%#{params[:q]}%")
+ }
+ end
+
+ def fields
+ field :id, as: :id
+ field :name, as: :text
+ end
+end
diff --git a/app/avo/resources/gem_name_reservation_resource.rb b/app/avo/resources/gem_name_reservation_resource.rb
deleted file mode 100644
index 0b44fa49e59..00000000000
--- a/app/avo/resources/gem_name_reservation_resource.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-class GemNameReservationResource < Avo::BaseResource
- self.title = :name
- self.includes = []
- self.search_query = lambda {
- scope.where("name LIKE ?", "%#{params[:q]}%")
- }
-
- field :id, as: :id
- field :name, as: :text
-end
diff --git a/app/avo/resources/gem_typo_exception.rb b/app/avo/resources/gem_typo_exception.rb
new file mode 100644
index 00000000000..7015f359dc6
--- /dev/null
+++ b/app/avo/resources/gem_typo_exception.rb
@@ -0,0 +1,19 @@
+class Avo::Resources::GemTypoException < Avo::BaseResource
+ self.title = :name
+ self.includes = []
+ if Gem.loaded_specs["avo-pro"]
+ self.search_query = lambda {
+ query.where("name ILIKE ?", "%#{params[:q]}%")
+ }
+ end
+
+ def fields
+ field :id, as: :id, hide_on: :index
+
+ field :name, as: :text, link_to_resource: true
+ field :info, as: :textarea
+
+ field :created_at, as: :date_time, sortable: true, readonly: true, only_on: %i[index show]
+ field :updated_at, as: :date_time, sortable: true, readonly: true, only_on: %i[index show]
+ end
+end
diff --git a/app/avo/resources/gem_typo_exception_resource.rb b/app/avo/resources/gem_typo_exception_resource.rb
deleted file mode 100644
index 8512e03d2f3..00000000000
--- a/app/avo/resources/gem_typo_exception_resource.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-class GemTypoExceptionResource < Avo::BaseResource
- self.title = :name
- self.includes = []
- self.search_query = lambda {
- scope.where("name ILIKE ?", "%#{params[:q]}%")
- }
-
- field :id, as: :id, hide_on: :index
- # Fields generated from the model
- field :name, as: :text, link_to_resource: true
- field :info, as: :textarea
- # add fields here
- field :created_at, as: :date_time, sortable: true, readonly: true, only_on: %i[index show]
- field :updated_at, as: :date_time, sortable: true, readonly: true, only_on: %i[index show]
-end
diff --git a/app/avo/resources/link_verification.rb b/app/avo/resources/link_verification.rb
new file mode 100644
index 00000000000..90bf8b12481
--- /dev/null
+++ b/app/avo/resources/link_verification.rb
@@ -0,0 +1,17 @@
+class Avo::Resources::LinkVerification < Avo::BaseResource
+ self.title = :id
+ self.includes = []
+
+ def fields
+ field :id, as: :id
+
+ field :linkable, as: :belongs_to,
+ polymorphic_as: :linkable,
+ types: [::Rubygem]
+ field :uri, as: :text
+ field :verified?, as: :boolean
+ field :last_verified_at, as: :date_time
+ field :last_failure_at, as: :date_time
+ field :failures_since_last_verification, as: :number
+ end
+end
diff --git a/app/avo/resources/link_verification_resource.rb b/app/avo/resources/link_verification_resource.rb
deleted file mode 100644
index 19806ea613e..00000000000
--- a/app/avo/resources/link_verification_resource.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-class LinkVerificationResource < Avo::BaseResource
- self.title = :id
- self.includes = []
- # self.search_query = -> do
- # scope.ransack(id_eq: params[:q], m: "or").result(distinct: false)
- # end
-
- field :id, as: :id
- # Fields generated from the model
- field :linkable, as: :belongs_to,
- polymorphic_as: :linkable,
- types: [::Rubygem]
- field :uri, as: :text
- field :verified?, as: :boolean
- field :last_verified_at, as: :date_time
- field :last_failure_at, as: :date_time
- field :failures_since_last_verification, as: :number
- # add fields here
-end
diff --git a/app/avo/resources/linkset.rb b/app/avo/resources/linkset.rb
new file mode 100644
index 00000000000..7498288e8f9
--- /dev/null
+++ b/app/avo/resources/linkset.rb
@@ -0,0 +1,14 @@
+class Avo::Resources::Linkset < Avo::BaseResource
+ self.title = :id
+ self.includes = [:rubygem]
+ self.visible_on_sidebar = false
+
+ def fields
+ field :id, as: :id, link_to_resource: true
+ field :rubygem, as: :belongs_to
+
+ Linkset::LINKS.each do |link|
+ field link, as: :text, format_using: -> { link_to value, value if value.present? }
+ end
+ end
+end
diff --git a/app/avo/resources/linkset_resource.rb b/app/avo/resources/linkset_resource.rb
deleted file mode 100644
index ac5966a45bf..00000000000
--- a/app/avo/resources/linkset_resource.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class LinksetResource < Avo::BaseResource
- self.title = :id
- self.includes = [:rubygem]
- self.visible_on_sidebar = false
-
- field :id, as: :id, link_to_resource: true
- field :rubygem, as: :belongs_to
-
- Linkset::LINKS.each do |link|
- field link, as: :text, format_using: -> { link_to value, value if value.present? }
- end
-end
diff --git a/app/avo/resources/log_ticket.rb b/app/avo/resources/log_ticket.rb
new file mode 100644
index 00000000000..893f8382a2b
--- /dev/null
+++ b/app/avo/resources/log_ticket.rb
@@ -0,0 +1,22 @@
+class Avo::Resources::LogTicket < Avo::BaseResource
+ self.title = :id
+ self.includes = []
+
+ class BackendFilter < Avo::Filters::ScopeBooleanFilter; end
+ class StatusFilter < Avo::Filters::ScopeBooleanFilter; end
+
+ def filters
+ filter BackendFilter, arguments: { default: LogTicket.backends.transform_values { true } }
+ filter StatusFilter, arguments: { default: LogTicket.statuses.transform_values { true } }
+ end
+
+ def fields
+ field :id, as: :id, link_to_resource: true
+
+ field :key, as: :text
+ field :directory, as: :text
+ field :backend, as: :select, enum: LogTicket.backends
+ field :status, as: :select, enum: LogTicket.statuses
+ field :processed_count, as: :number, sortable: true
+ end
+end
diff --git a/app/avo/resources/log_ticket_resource.rb b/app/avo/resources/log_ticket_resource.rb
deleted file mode 100644
index 1a6c88419e2..00000000000
--- a/app/avo/resources/log_ticket_resource.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-class LogTicketResource < Avo::BaseResource
- self.title = :id
- self.includes = []
-
- class BackendFilter < ScopeBooleanFilter; end
- filter BackendFilter, arguments: { default: LogTicket.backends.transform_values { true } }
-
- class StatusFilter < ScopeBooleanFilter; end
- filter StatusFilter, arguments: { default: LogTicket.statuses.transform_values { true } }
-
- field :id, as: :id, link_to_resource: true
-
- field :key, as: :text
- field :directory, as: :text
- field :backend, as: :select, enum: LogTicket.backends
- field :status, as: :select, enum: LogTicket.statuses
- field :processed_count, as: :number, sortable: true
-end
diff --git a/app/avo/resources/maintenance_tasks_run.rb b/app/avo/resources/maintenance_tasks_run.rb
new file mode 100644
index 00000000000..623519b2de5
--- /dev/null
+++ b/app/avo/resources/maintenance_tasks_run.rb
@@ -0,0 +1,30 @@
+class Avo::Resources::MaintenanceTasksRun < Avo::BaseResource
+ self.title = :id
+ self.includes = []
+ self.model_class = ::MaintenanceTasks::Run
+
+ class StatusFilter < Avo::Filters::ScopeBooleanFilter; end
+
+ def filters
+ filter StatusFilter, arguments: { default: MaintenanceTasks::Run.statuses.transform_values { true } }
+ end
+
+ def fields
+ field :id, as: :id
+
+ field :task_name, as: :text
+ field :started_at, as: :date_time, sortable: true
+ field :ended_at, as: :date_time, sortable: true
+ field :time_running, as: :number, sortable: true
+ field :tick_count, as: :number
+ field :tick_total, as: :number
+ field :job_id, as: :text
+ field :cursor, as: :number
+ field :status, as: :select, enum: MaintenanceTasks::Run.statuses
+ field :error_class, as: :text
+ field :error_message, as: :text
+ field :backtrace, as: :textarea
+ field :arguments, as: :textarea
+ field :lock_version, as: :number
+ end
+end
diff --git a/app/avo/resources/maintenance_tasks_run_resource.rb b/app/avo/resources/maintenance_tasks_run_resource.rb
deleted file mode 100644
index 0f553fdb3a5..00000000000
--- a/app/avo/resources/maintenance_tasks_run_resource.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-class MaintenanceTasksRunResource < Avo::BaseResource
- self.title = :id
- self.includes = []
- self.model_class = ::MaintenanceTasks::Run
- # self.search_query = -> do
- # scope.ransack(id_eq: params[:q], m: "or").result(distinct: false)
- # end
-
- class StatusFilter < ScopeBooleanFilter; end
- filter StatusFilter, arguments: { default: MaintenanceTasks::Run.statuses.transform_values { true } }
-
- field :id, as: :id
- # Fields generated from the model
- field :task_name, as: :text
- field :started_at, as: :date_time, sortable: true
- field :ended_at, as: :date_time, sortable: true
- field :time_running, as: :number, sortable: true
- field :tick_count, as: :number
- field :tick_total, as: :number
- field :job_id, as: :text
- field :cursor, as: :number
- field :status, as: :select, enum: MaintenanceTasks::Run.statuses
- field :error_class, as: :text
- field :error_message, as: :text
- field :backtrace, as: :textarea
- field :arguments, as: :textarea
- field :lock_version, as: :number
- # add fields here
-end
diff --git a/app/avo/resources/oidc_api_key_role.rb b/app/avo/resources/oidc_api_key_role.rb
new file mode 100644
index 00000000000..8dc81b51b6a
--- /dev/null
+++ b/app/avo/resources/oidc_api_key_role.rb
@@ -0,0 +1,34 @@
+class Avo::Resources::OIDCApiKeyRole < Avo::BaseResource
+ self.title = :token
+ self.includes = []
+ self.model_class = ::OIDC::ApiKeyRole
+
+ def fields
+ field :token, as: :text, link_to_resource: true, readonly: true
+ field :id, as: :id, link_to_resource: true, hide_on: :index
+ # Fields generated from the model
+ field :name, as: :text
+ field :provider, as: :belongs_to
+ field :user, as: :belongs_to, searchable: true
+ field :api_key_permissions, as: :nested do
+ field :valid_for, as: :text, format_using: -> { value&.iso8601 }
+ field :scopes, as: :tags, suggestions: ApiKey::API_SCOPES.map { { label: _1, value: _1 } }, enforce_suggestions: true
+ field :gems, as: :tags, suggestions: -> { Rubygem.limit(10).pluck(:name).map { { value: _1, label: _1 } } }
+ end
+ field :access_policy, as: :nested do
+ field :statements, as: :array_of, field: :nested do
+ field :effect, as: :select, options: { "Allow" => "allow" }, default: "Allow"
+ field :principal, as: :nested, field_options: { stacked: false } do
+ field :oidc, as: :text
+ end
+ field :conditions, as: :array_of, field: :nested, field_options: { stacked: false } do
+ field :operator, as: :select, options: OIDC::AccessPolicy::Statement::Condition::OPERATORS.index_by(&:titleize)
+ field :claim, as: :text
+ field :value, as: :text
+ end
+ end
+ end
+
+ field :id_tokens, as: :has_many
+ end
+end
diff --git a/app/avo/resources/oidc_api_key_role_resource.rb b/app/avo/resources/oidc_api_key_role_resource.rb
deleted file mode 100644
index 3f59087e912..00000000000
--- a/app/avo/resources/oidc_api_key_role_resource.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-class OIDCApiKeyRoleResource < Avo::BaseResource
- self.title = :token
- self.includes = []
- self.model_class = ::OIDC::ApiKeyRole
- # self.search_query = -> do
- # scope.ransack(id_eq: params[:q], m: "or").result(distinct: false)
- # end
-
- field :token, as: :text, link_to_resource: true, readonly: true
- field :id, as: :id, link_to_resource: true, hide_on: :index
- # Fields generated from the model
- field :name, as: :text
- field :provider, as: :belongs_to
- field :user, as: :belongs_to, searchable: true
- field :api_key_permissions, as: :nested do
- field :valid_for, as: :text, format_using: -> { value&.iso8601 }
- field :scopes, as: :tags, suggestions: ApiKey::API_SCOPES.map { { label: _1, value: _1 } }, enforce_suggestions: true
- field :gems, as: :tags, suggestions: -> { Rubygem.limit(10).pluck(:name).map { { value: _1, label: _1 } } }
- end
- field :access_policy, as: :nested do
- field :statements, as: :array_of, field: :nested do
- field :effect, as: :select, options: { "Allow" => "allow" }, default: "Allow"
- field :principal, as: :nested, field_options: { stacked: false } do
- field :oidc, as: :text
- end
- field :conditions, as: :array_of, field: :nested, field_options: { stacked: false } do
- field :operator, as: :select, options: OIDC::AccessPolicy::Statement::Condition::OPERATORS.index_by(&:titleize)
- field :claim, as: :text
- field :value, as: :text
- end
- end
- end
-
- field :id_tokens, as: :has_many
- # add fields here
-end
diff --git a/app/avo/resources/oidc_id_token.rb b/app/avo/resources/oidc_id_token.rb
new file mode 100644
index 00000000000..48ae92fe2c0
--- /dev/null
+++ b/app/avo/resources/oidc_id_token.rb
@@ -0,0 +1,21 @@
+class Avo::Resources::OIDCIdToken < Avo::BaseResource
+ self.title = :id
+ self.includes = []
+ self.model_class = ::OIDC::IdToken
+
+ def fields
+ field :id, as: :id
+ # Fields generated from the model
+ field :api_key_role, as: :belongs_to
+ field :provider, as: :has_one
+ field :api_key, as: :has_one
+
+ field :jwt, as: :heading
+ field :claims, as: :key_value, stacked: true do
+ record.jwt.fetch("claims")
+ end
+ field :header, as: :key_value, stacked: true do
+ record.jwt.fetch("header")
+ end
+ end
+end
diff --git a/app/avo/resources/oidc_id_token_resource.rb b/app/avo/resources/oidc_id_token_resource.rb
deleted file mode 100644
index bda90ed7025..00000000000
--- a/app/avo/resources/oidc_id_token_resource.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-class OIDCIdTokenResource < Avo::BaseResource
- self.title = :id
- self.includes = []
- self.model_class = ::OIDC::IdToken
- # self.search_query = -> do
- # scope.ransack(id_eq: params[:q], m: "or").result(distinct: false)
- # end
-
- field :id, as: :id
- # Fields generated from the model
- field :api_key_role, as: :belongs_to
- field :provider, as: :has_one
- field :api_key, as: :has_one
-
- heading "JWT"
- field :claims, as: :key_value, stacked: true do
- model.jwt.fetch("claims")
- end
- field :header, as: :key_value, stacked: true do
- model.jwt.fetch("header")
- end
- # add fields here
-end
diff --git a/app/avo/resources/oidc_pending_trusted_publisher.rb b/app/avo/resources/oidc_pending_trusted_publisher.rb
new file mode 100644
index 00000000000..885166c0818
--- /dev/null
+++ b/app/avo/resources/oidc_pending_trusted_publisher.rb
@@ -0,0 +1,20 @@
+class Avo::Resources::OIDCPendingTrustedPublisher < Avo::BaseResource
+ self.title = :id
+ self.includes = []
+ self.model_class = ::OIDC::PendingTrustedPublisher
+
+ class ExpiredFilter < Avo::Filters::ScopeBooleanFilter; end
+
+ def filters
+ filter ExpiredFilter, arguments: { default: { expired: false, unexpired: true } }
+ end
+
+ def fields
+ field :id, as: :id
+
+ field :rubygem_name, as: :text
+ field :user, as: :belongs_to
+ field :trusted_publisher, as: :belongs_to, polymorphic_as: :trusted_publisher
+ field :expires_at, as: :date_time
+ end
+end
diff --git a/app/avo/resources/oidc_pending_trusted_publisher_resource.rb b/app/avo/resources/oidc_pending_trusted_publisher_resource.rb
deleted file mode 100644
index f546d3a8df1..00000000000
--- a/app/avo/resources/oidc_pending_trusted_publisher_resource.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-class OIDCPendingTrustedPublisherResource < Avo::BaseResource
- self.title = :id
- self.includes = []
- self.model_class = ::OIDC::PendingTrustedPublisher
-
- class ExpiredFilter < ScopeBooleanFilter; end
- filter ExpiredFilter, arguments: { default: { expired: false, unexpired: true } }
-
- field :id, as: :id
- # Fields generated from the model
- field :rubygem_name, as: :text
- field :user, as: :belongs_to
- field :trusted_publisher, as: :belongs_to, polymorphic_as: :trusted_publisher
- field :expires_at, as: :date_time
- # add fields here
-end
diff --git a/app/avo/resources/oidc_provider.rb b/app/avo/resources/oidc_provider.rb
new file mode 100644
index 00000000000..74cf4d35f10
--- /dev/null
+++ b/app/avo/resources/oidc_provider.rb
@@ -0,0 +1,23 @@
+class Avo::Resources::OIDCProvider < Avo::BaseResource
+ self.title = :issuer
+ self.includes = []
+ self.model_class = ::OIDC::Provider
+
+ def actions
+ action Avo::Actions::RefreshOIDCProvider
+ end
+
+ def fields
+ field :issuer, as: :text, link_to_resource: true
+ field :configuration, as: :nested do
+ visible_on = %i[edit new]
+ OIDC::Provider::Configuration.then { (_1.required_attributes + _1.optional_attributes) - fields.map(&:id) }.each do |k|
+ field k, as: (k.to_s.end_with?("s_supported") ? :tags : :text), visible: ->(_) { visible_on.include?(view) || value.send(k).present? }
+ end
+ end
+ field :jwks, as: :array_of, field: :json_viewer, hide_on: :index
+ field :api_key_roles, as: :has_many
+
+ field :id, as: :id
+ end
+end
diff --git a/app/avo/resources/oidc_provider_resource.rb b/app/avo/resources/oidc_provider_resource.rb
deleted file mode 100644
index 8a2fa0fe6d4..00000000000
--- a/app/avo/resources/oidc_provider_resource.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-class OIDCProviderResource < Avo::BaseResource
- self.title = :issuer
- self.includes = []
- self.model_class = ::OIDC::Provider
-
- action RefreshOIDCProvider
-
- # Fields generated from the model
- field :issuer, as: :text, link_to_resource: true
- field :configuration, as: :nested do
- visible_on = %i[edit new]
- OIDC::Provider::Configuration.then { (_1.required_attributes + _1.optional_attributes) - fields.map(&:id) }.each do |k|
- field k, as: (k.to_s.end_with?("s_supported") ? :tags : :text), visible: ->(_) { visible_on.include?(view) || value.send(k).present? }
- end
- end
- field :jwks, as: :array_of, field: :json_viewer, hide_on: :index
- field :api_key_roles, as: :has_many
- # add fields here
- field :id, as: :id
-end
diff --git a/app/avo/resources/oidc_rubygem_trusted_publisher.rb b/app/avo/resources/oidc_rubygem_trusted_publisher.rb
new file mode 100644
index 00000000000..e7e9d13f532
--- /dev/null
+++ b/app/avo/resources/oidc_rubygem_trusted_publisher.rb
@@ -0,0 +1,12 @@
+class Avo::Resources::OIDCRubygemTrustedPublisher < Avo::BaseResource
+ self.title = :id
+ self.includes = [:trusted_publisher]
+ self.model_class = ::OIDC::RubygemTrustedPublisher
+
+ def fields
+ field :id, as: :id
+
+ field :rubygem, as: :belongs_to
+ field :trusted_publisher, as: :belongs_to, polymorphic_as: :trusted_publisher
+ end
+end
diff --git a/app/avo/resources/oidc_rubygem_trusted_publisher_resource.rb b/app/avo/resources/oidc_rubygem_trusted_publisher_resource.rb
deleted file mode 100644
index 96c63c43096..00000000000
--- a/app/avo/resources/oidc_rubygem_trusted_publisher_resource.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-class OIDCRubygemTrustedPublisherResource < Avo::BaseResource
- self.title = :id
- self.includes = [:trusted_publisher]
- self.model_class = ::OIDC::RubygemTrustedPublisher
-
- field :id, as: :id
- # Fields generated from the model
- field :rubygem, as: :belongs_to
- field :trusted_publisher, as: :belongs_to, polymorphic_as: :trusted_publisher
- # add fields here
-end
diff --git a/app/avo/resources/oidc_trusted_publisher_github_action.rb b/app/avo/resources/oidc_trusted_publisher_github_action.rb
new file mode 100644
index 00000000000..0a850ed2e21
--- /dev/null
+++ b/app/avo/resources/oidc_trusted_publisher_github_action.rb
@@ -0,0 +1,20 @@
+class Avo::Resources::OIDCTrustedPublisherGitHubAction < Avo::BaseResource
+ self.title = :name
+ self.includes = []
+ self.model_class = ::OIDC::TrustedPublisher::GitHubAction
+
+ def fields
+ field :id, as: :id
+
+ field :repository_owner, as: :text
+ field :repository_name, as: :text
+ field :repository_owner_id, as: :text
+ field :workflow_filename, as: :text
+ field :environment, as: :text
+
+ field :rubygem_trusted_publishers, as: :has_many
+ field :pending_trusted_publishers, as: :has_many
+ field :rubygems, as: :has_many, through: :rubygem_trusted_publishers
+ field :api_keys, as: :has_many, inverse_of: :owner
+ end
+end
diff --git a/app/avo/resources/oidc_trusted_publisher_github_action_resource.rb b/app/avo/resources/oidc_trusted_publisher_github_action_resource.rb
deleted file mode 100644
index 0ea00fd83e4..00000000000
--- a/app/avo/resources/oidc_trusted_publisher_github_action_resource.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-class OIDCTrustedPublisherGitHubActionResource < Avo::BaseResource
- self.title = :name
- self.includes = []
- self.model_class = ::OIDC::TrustedPublisher::GitHubAction
-
- field :id, as: :id
- # Fields generated from the model
- field :repository_owner, as: :text
- field :repository_name, as: :text
- field :repository_owner_id, as: :text
- field :workflow_filename, as: :text
- field :environment, as: :text
- # add fields here
- #
- field :rubygem_trusted_publishers, as: :has_many
- field :pending_trusted_publishers, as: :has_many
- field :rubygems, as: :has_many, through: :rubygem_trusted_publishers
- field :api_keys, as: :has_many, inverse_of: :owner
-end
diff --git a/app/avo/resources/ownership.rb b/app/avo/resources/ownership.rb
new file mode 100644
index 00000000000..3c45bf27da1
--- /dev/null
+++ b/app/avo/resources/ownership.rb
@@ -0,0 +1,34 @@
+class Avo::Resources::Ownership < Avo::BaseResource
+ self.title = :cache_key
+ self.includes = []
+
+ class ConfirmedFilter < Avo::Filters::ScopeBooleanFilter; end
+
+ def filters
+ filter ConfirmedFilter, arguments: { default: { confirmed: true, unconfirmed: true } }
+ end
+
+ def fields
+ field :id, as: :id, link_to_resource: true
+
+ field :user, as: :belongs_to
+ field :rubygem, as: :belongs_to
+
+ field :token, as: :heading
+
+ field :token, as: :text, visible: -> { false }
+ field :token_expires_at, as: :date_time
+ field :api_key_rubygem_scopes, as: :has_many
+
+ field :notifications, as: :heading
+
+ field :push_notifier, as: :boolean
+ field :owner_notifier, as: :boolean
+ field :ownership_request_notifier, as: :boolean
+
+ field :authorization, as: :heading
+
+ field :authorizer, as: :belongs_to
+ field :confirmed_at, as: :date_time
+ end
+end
diff --git a/app/avo/resources/ownership_resource.rb b/app/avo/resources/ownership_resource.rb
deleted file mode 100644
index cd51054bbd0..00000000000
--- a/app/avo/resources/ownership_resource.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-class OwnershipResource < Avo::BaseResource
- self.title = :cache_key
- self.includes = []
-
- class ConfirmedFilter < ScopeBooleanFilter; end
- filter ConfirmedFilter, arguments: { default: { confirmed: true, unconfirmed: true } }
-
- field :id, as: :id, link_to_resource: true
-
- field :user, as: :belongs_to
- field :rubygem, as: :belongs_to
-
- heading "Token"
-
- field :token, as: :text, visible: ->(_) { false }
- field :token_expires_at, as: :date_time
- field :api_key_rubygem_scopes, as: :has_many
-
- heading "Notifications"
-
- field :push_notifier, as: :boolean
- field :owner_notifier, as: :boolean
- field :ownership_request_notifier, as: :boolean
-
- heading "Authorization"
-
- field :authorizer, as: :belongs_to
- field :confirmed_at, as: :date_time
-end
diff --git a/app/avo/resources/rubygem.rb b/app/avo/resources/rubygem.rb
new file mode 100644
index 00000000000..5c470ef31a4
--- /dev/null
+++ b/app/avo/resources/rubygem.rb
@@ -0,0 +1,54 @@
+class Avo::Resources::Rubygem < Avo::BaseResource
+ self.title = :name
+ self.includes = []
+ if Gem.loaded_specs["avo-pro"]
+ self.search_query = lambda {
+ query.where("name LIKE ?", "%#{params[:q]}%")
+ }
+ end
+
+ def actions
+ action Avo::Actions::ReleaseReservedNamespace
+ action Avo::Actions::AddOwner
+ action Avo::Actions::YankRubygem
+ action Avo::Actions::UploadInfoFile
+ action Avo::Actions::UploadNamesFile
+ action Avo::Actions::UploadVersionsFile
+ end
+
+ class IndexedFilter < Avo::Filters::ScopeBooleanFilter; end
+
+ def filters
+ filter IndexedFilter, arguments: { default: { with_versions: true, without_versions: true } }
+ end
+
+ def fields
+ field :name, as: :text, link_to_resource: true
+ field :indexed, as: :boolean
+ field :slug, as: :text, hide_on: :index
+ field :id, as: :id, hide_on: :index
+ field :protected_days, as: :number, hide_on: :index
+
+ tabs style: :pills do
+ field :versions, as: :has_many
+ field :latest_version, as: :has_one
+
+ field :ownerships, as: :has_many
+ field :ownerships_including_unconfirmed, as: :has_many
+ field :ownership_calls, as: :has_many
+ field :ownership_requests, as: :has_many
+
+ field :subscriptions, as: :has_many
+ field :subscribers, as: :has_many, through: :subscriptions
+
+ field :web_hooks, as: :has_many
+ field :linkset, as: :has_one
+ field :gem_download, as: :has_one
+
+ field :link_verifications, as: :has_many
+ field :oidc_rubygem_trusted_publishers, as: :has_many
+
+ field :audits, as: :has_many
+ end
+ end
+end
diff --git a/app/avo/resources/rubygem_resource.rb b/app/avo/resources/rubygem_resource.rb
deleted file mode 100644
index e942e207847..00000000000
--- a/app/avo/resources/rubygem_resource.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-class RubygemResource < Avo::BaseResource
- self.title = :name
- self.includes = []
- self.search_query = lambda {
- scope.where("name LIKE ?", "%#{params[:q]}%")
- }
-
- action ReleaseReservedNamespace
- action AddOwner
- action YankRubygem
- action UploadInfoFile
- action UploadNamesFile
- action UploadVersionsFile
-
- class IndexedFilter < ScopeBooleanFilter; end
- filter IndexedFilter, arguments: { default: { with_versions: true, without_versions: true } }
-
- # Fields generated from the model
- field :name, as: :text, link_to_resource: true
- field :indexed, as: :boolean
- field :slug, as: :text, hide_on: :index
- field :id, as: :id, hide_on: :index
- field :protected_days, as: :number, hide_on: :index
-
- tabs style: :pills do
- field :versions, as: :has_many
- field :latest_version, as: :has_one
-
- field :ownerships, as: :has_many
- field :ownerships_including_unconfirmed, as: :has_many
- field :ownership_calls, as: :has_many
- field :ownership_requests, as: :has_many
-
- field :subscriptions, as: :has_many
- field :subscribers, as: :has_many, through: :subscriptions
-
- field :web_hooks, as: :has_many
- field :linkset, as: :has_one
- field :gem_download, as: :has_one
-
- field :link_verifications, as: :has_many
- field :oidc_rubygem_trusted_publishers, as: :has_many
-
- field :audits, as: :has_many
- end
-end
diff --git a/app/avo/resources/sendgrid_event.rb b/app/avo/resources/sendgrid_event.rb
new file mode 100644
index 00000000000..c9d57d8a636
--- /dev/null
+++ b/app/avo/resources/sendgrid_event.rb
@@ -0,0 +1,27 @@
+class Avo::Resources::SendgridEvent < Avo::BaseResource
+ self.title = :sendgrid_id
+ self.includes = []
+ # self.search_query = -> do
+ # query.ransack(id_eq: params[:q], m: "or").result(distinct: false)
+ # end
+
+ class StatusFilter < Avo::Filters::ScopeBooleanFilter; end
+ class EventTypeFilter < Avo::Filters::ScopeBooleanFilter; end
+
+ def filters
+ filter StatusFilter, arguments: { default: SendgridEvent.statuses.transform_values { true } }
+ filter EventTypeFilter, arguments: { default: SendgridEvent.event_types.transform_values { true } }
+ filter Avo::Filters::EmailFilter
+ end
+
+ def fields
+ field :id, as: :id, hide_on: :index
+
+ field :sendgrid_id, as: :text, link_to_resource: true
+ field :email, as: :text
+ field :event_type, as: :text
+ field :occurred_at, as: :date_time, sortable: true
+ field :payload, as: :json_viewer
+ field :status, as: :select, enum: SendgridEvent.statuses
+ end
+end
diff --git a/app/avo/resources/sendgrid_event_resource.rb b/app/avo/resources/sendgrid_event_resource.rb
deleted file mode 100644
index 9328ed102db..00000000000
--- a/app/avo/resources/sendgrid_event_resource.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-class SendgridEventResource < Avo::BaseResource
- self.title = :sendgrid_id
- self.includes = []
- # self.search_query = -> do
- # scope.ransack(id_eq: params[:q], m: "or").result(distinct: false)
- # end
-
- class StatusFilter < ScopeBooleanFilter; end
- filter StatusFilter, arguments: { default: SendgridEvent.statuses.transform_values { true } }
-
- class EventTypeFilter < ScopeBooleanFilter; end
- filter EventTypeFilter, arguments: { default: SendgridEvent.event_types.transform_values { true } }
-
- filter EmailFilter
-
- field :id, as: :id, hide_on: :index
- # Fields generated from the model
- field :sendgrid_id, as: :text, link_to_resource: true
- field :email, as: :text
- field :event_type, as: :text
- field :occurred_at, as: :date_time, sortable: true
- field :payload, as: :json_viewer
- field :status, as: :select, enum: SendgridEvent.statuses
- # add fields here
-end
diff --git a/app/avo/resources/user.rb b/app/avo/resources/user.rb
new file mode 100644
index 00000000000..4c55562a940
--- /dev/null
+++ b/app/avo/resources/user.rb
@@ -0,0 +1,77 @@
+class Avo::Resources::User < Avo::BaseResource
+ self.title = :name
+ self.includes = []
+ if Gem.loaded_specs["avo-pro"]
+ self.search_query = lambda {
+ query.where("email LIKE ? OR handle LIKE ?", "%#{params[:q]}%", "%#{params[:q]}%")
+ }
+ end
+
+ def actions
+ action Avo::Actions::BlockUser
+ action Avo::Actions::CreateUser
+ action Avo::Actions::ChangeUserEmail
+ action Avo::Actions::ResetApiKey
+ action Avo::Actions::ResetUser2fa
+ action Avo::Actions::YankRubygemsForUser
+ action Avo::Actions::YankUser
+ end
+
+ def fields
+ field :id, as: :id
+
+ field :email, as: :text
+ field :gravatar,
+ as: :gravatar,
+ rounded: true,
+ size: 48 do |_, _, _|
+ record.email
+ end
+
+ field :email_confirmed, as: :boolean
+
+ field :email_reset, as: :boolean
+ field :handle, as: :text
+ field :public_email, as: :boolean
+ field :twitter_username, as: :text, as_html: true, format_using: -> { link_to value, "https://twitter.com/#{value}", target: :_blank, rel: :noopener if value.present? }
+ field :unconfirmed_email, as: :text
+
+ field :mail_fails, as: :number
+ field :blocked_email, as: :text
+
+ tabs style: :pills do
+ tab "Auth" do
+ field :encrypted_password, as: :password, visible: -> { false }
+ field :totp_seed, as: :text, visible: -> { false }
+ field :mfa_seed, as: :text, visible: -> { false } # legacy field
+ field :mfa_level, as: :select, enum: ::User.mfa_levels
+ field :mfa_recovery_codes, as: :text, visible: -> { false }
+ field :mfa_hashed_recovery_codes, as: :text, visible: -> { false }
+ field :webauthn_id, as: :text
+ field :remember_token_expires_at, as: :date_time
+ field :api_key, as: :text, visible: -> { false }
+ field :confirmation_token, as: :text, visible: -> { false }
+ field :remember_token, as: :text, visible: -> { false }
+ field :salt, as: :text, visible: -> { false }
+ field :token, as: :text, visible: -> { false }
+ field :token_expires_at, as: :date_time
+ end
+ field :ownerships, as: :has_many
+ field :rubygems, as: :has_many, through: :ownerships
+ field :subscriptions, as: :has_many
+ field :subscribed_gems, as: :has_many, through: :subscriptions
+ field :deletions, as: :has_many
+ field :web_hooks, as: :has_many
+ field :unconfirmed_ownerships, as: :has_many
+ field :api_keys, as: :has_many, name: "API Keys"
+ field :ownership_calls, as: :has_many
+ field :ownership_requests, as: :has_many
+ field :pushed_versions, as: :has_many
+ field :oidc_api_key_roles, as: :has_many
+ field :webauthn_credentials, as: :has_many
+ field :webauthn_verification, as: :has_one
+
+ field :audits, as: :has_many
+ end
+ end
+end
diff --git a/app/avo/resources/user_resource.rb b/app/avo/resources/user_resource.rb
deleted file mode 100644
index 71c93636dc0..00000000000
--- a/app/avo/resources/user_resource.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-class UserResource < Avo::BaseResource
- self.title = :name
- self.includes = []
- self.search_query = lambda {
- scope.where("email LIKE ? OR handle LIKE ?", "%#{params[:q]}%", "%#{params[:q]}%")
- }
-
- action BlockUser
- action CreateUser
- action ChangeUserEmail
- action ResetApiKey
- action ResetUser2fa
- action YankRubygemsForUser
- action YankUser
-
- field :id, as: :id
- # Fields generated from the model
- field :email, as: :text
- field :gravatar,
- as: :gravatar,
- rounded: true,
- size: 48 do |_, _, _|
- model.email
- end
-
- field :email_confirmed, as: :boolean
-
- field :email_reset, as: :boolean
- field :handle, as: :text
- field :public_email, as: :boolean
- field :twitter_username, as: :text, as_html: true, format_using: -> { link_to value, "https://twitter.com/#{value}", target: :_blank, rel: :noopener if value.present? }
- field :unconfirmed_email, as: :text
-
- field :mail_fails, as: :number
- field :blocked_email, as: :text
-
- tabs style: :pills do
- tab "Auth" do
- field :encrypted_password, as: :password, visible: ->(_) { false }
- field :totp_seed, as: :text, visible: ->(_) { false }
- field :mfa_seed, as: :text, visible: ->(_) { false } # legacy field
- field :mfa_level, as: :select, enum: ::User.mfa_levels
- field :mfa_recovery_codes, as: :text, visible: ->(_) { false }
- field :mfa_hashed_recovery_codes, as: :text, visible: ->(_) { false }
- field :webauthn_id, as: :text
- field :remember_token_expires_at, as: :date_time
- field :api_key, as: :text, visible: ->(_) { false }
- field :confirmation_token, as: :text, visible: ->(_) { false }
- field :remember_token, as: :text, visible: ->(_) { false }
- field :salt, as: :text, visible: ->(_) { false }
- field :token, as: :text, visible: ->(_) { false }
- field :token_expires_at, as: :date_time
- end
- field :ownerships, as: :has_many
- field :rubygems, as: :has_many, through: :ownerships
- field :subscriptions, as: :has_many
- field :subscribed_gems, as: :has_many, through: :subscriptions
- field :deletions, as: :has_many
- field :web_hooks, as: :has_many
- field :unconfirmed_ownerships, as: :has_many
- field :api_keys, as: :has_many, name: "API Keys"
- field :ownership_calls, as: :has_many
- field :ownership_requests, as: :has_many
- field :pushed_versions, as: :has_many
- field :oidc_api_key_roles, as: :has_many
- field :webauthn_credentials, as: :has_many
- field :webauthn_verification, as: :has_one
-
- field :audits, as: :has_many
- end
-end
diff --git a/app/avo/resources/version.rb b/app/avo/resources/version.rb
new file mode 100644
index 00000000000..e3bc36bf860
--- /dev/null
+++ b/app/avo/resources/version.rb
@@ -0,0 +1,78 @@
+class Avo::Resources::Version < Avo::BaseResource
+ self.title = :full_name
+ self.includes = [:rubygem]
+ if Gem.loaded_specs["avo-pro"]
+ self.search_query = lambda {
+ query.where("full_name LIKE ?", "#{params[:q]}%")
+ }
+ end
+
+ def actions
+ action Avo::Actions::RestoreVersion
+ end
+
+ class IndexedFilter < Avo::Filters::ScopeBooleanFilter; end
+
+ def filters
+ filter IndexedFilter, arguments: { default: { indexed: true, yanked: true } }
+ end
+
+ def fields
+ field :full_name, as: :text, link_to_resource: true
+ field :id, as: :id, hide_on: :index, as_html: true do |_id, *_args|
+ link_to record.id, main_app.rubygem_version_url(record.rubygem.slug, record.slug)
+ end
+
+ field :rubygem, as: :belongs_to
+ field :slug, as: :text, hide_on: :index
+ field :number, as: :text
+ field :platform, as: :text
+
+ field :canonical_number, as: :text
+
+ field :indexed, as: :boolean
+ field :prerelease, as: :boolean
+ field :position, as: :number
+ field :latest, as: :boolean
+
+ field :yanked_at, as: :date_time, sortable: true
+
+ field :pusher, as: :belongs_to, class: "User"
+ field :pusher_api_key, as: :belongs_to, class: "ApiKey"
+
+ tabs do
+ tab "Metadata", description: "Metadata that comes from the gemspec" do
+ panel do
+ field :summary, as: :textarea
+ field :description, as: :textarea
+ field :authors, as: :textarea
+ field :licenses, as: :textarea
+ field :cert_chain, as: :textarea
+ field :built_at, as: :date_time, sortable: true
+ field :metadata, as: :key_value, stacked: true
+ end
+ end
+
+ tab "Runtime information" do
+ panel do
+ field :size, as: :number, sortable: true
+ field :requirements, as: :textarea
+ field :required_ruby_version, as: :text
+ field :sha256, as: :text
+ field :required_rubygems_version, as: :text
+ end
+ end
+
+ tab "API" do
+ panel do
+ field :info_checksum, as: :text
+ field :yanked_info_checksum, as: :text
+ end
+ end
+
+ field :dependencies, as: :has_many
+ field :gem_download, as: :has_one, name: "Downloads"
+ field :deletion, as: :has_one
+ end
+ end
+end
diff --git a/app/avo/resources/version_resource.rb b/app/avo/resources/version_resource.rb
deleted file mode 100644
index 4334c6f1c89..00000000000
--- a/app/avo/resources/version_resource.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-class VersionResource < Avo::BaseResource
- self.title = :full_name
- self.includes = [:rubygem]
- self.search_query = lambda {
- scope.where("full_name LIKE ?", "#{params[:q]}%")
- }
-
- action RestoreVersion
-
- class IndexedFilter < ScopeBooleanFilter; end
- filter IndexedFilter, arguments: { default: { indexed: true, yanked: true } }
-
- field :full_name, as: :text, link_to_resource: true
- field :id, as: :id, hide_on: :index, as_html: true do |_id, *_args|
- link_to model.id, main_app.rubygem_version_url(model.rubygem.slug, model.slug)
- end
-
- field :rubygem, as: :belongs_to
- field :slug, as: :text, hide_on: :index
- field :number, as: :text
- field :platform, as: :text
-
- field :canonical_number, as: :text
-
- field :indexed, as: :boolean
- field :prerelease, as: :boolean
- field :position, as: :number
- field :latest, as: :boolean
-
- field :yanked_at, as: :date_time, sortable: true
-
- field :pusher, as: :belongs_to, class: "User"
- field :pusher_api_key, as: :belongs_to, class: "ApiKey"
-
- tabs do
- tab "Metadata", description: "Metadata that comes from the gemspec" do
- panel do
- field :summary, as: :textarea
- field :description, as: :textarea
- field :authors, as: :textarea
- field :licenses, as: :textarea
- field :cert_chain, as: :textarea
- field :built_at, as: :date_time, sortable: true
- field :metadata, as: :key_value, stacked: true
- end
- end
-
- tab "Runtime information" do
- panel do
- field :size, as: :number, sortable: true
- field :requirements, as: :textarea
- field :required_ruby_version, as: :text
- field :sha256, as: :text
- field :required_rubygems_version, as: :text
- end
- end
-
- tab "API" do
- panel do
- field :info_checksum, as: :text
- field :yanked_info_checksum, as: :text
- end
- end
-
- field :dependencies, as: :has_many
- field :gem_download, as: :has_one, name: "Downloads"
- field :deletion, as: :has_one
- end
-end
diff --git a/app/avo/resources/web_hook.rb b/app/avo/resources/web_hook.rb
new file mode 100644
index 00000000000..98ef03cdbef
--- /dev/null
+++ b/app/avo/resources/web_hook.rb
@@ -0,0 +1,43 @@
+class Avo::Resources::WebHook < Avo::BaseResource
+ self.title = :id
+ self.includes = %i[user rubygem]
+
+ def actions
+ action Avo::Actions::DeleteWebhook
+ end
+
+ class EnabledFilter < Avo::Filters::ScopeBooleanFilter; end
+ class GlobalFilter < Avo::Filters::ScopeBooleanFilter; end
+
+ def filters
+ filter EnabledFilter, arguments: { default: { enabled: true, disabled: false } }
+ filter GlobalFilter, arguments: { default: { global: true, specific: true } }
+ end
+
+ def fields
+ field :id, as: :id, link_to_resource: true
+
+ field :url, as: :text
+ field :enabled?, as: :boolean
+ field :failure_count, as: :number, sortable: true, index_text_align: :right
+ field :user, as: :belongs_to
+ field :rubygem, as: :belongs_to
+ field :global?, as: :boolean
+
+ field :hook_relay_stream, as: :text do
+ stream_name = "webhook_id-#{record.id}"
+ link_to stream_name, "https://app.hookrelay.dev/hooks/#{ENV['HOOK_RELAY_STREAM_ID']}?started_at=P1W&stream=#{stream_name}"
+ end
+
+ field :disabled_reason, as: :text
+ field :disabled_at, as: :date_time, sortable: true
+ field :last_success, as: :date_time, sortable: true
+ field :last_failure, as: :date_time, sortable: true
+ field :successes_since_last_failure, as: :number, sortable: true
+ field :failures_since_last_success, as: :number, sortable: true
+
+ tabs style: :pills do
+ field :audits, as: :has_many
+ end
+ end
+end
diff --git a/app/avo/resources/web_hook_resource.rb b/app/avo/resources/web_hook_resource.rb
deleted file mode 100644
index ee2c22d59f9..00000000000
--- a/app/avo/resources/web_hook_resource.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-class WebHookResource < Avo::BaseResource
- self.title = :id
- self.includes = %i[user rubygem]
-
- action DeleteWebhook
- class EnabledFilter < ScopeBooleanFilter; end
- filter EnabledFilter, arguments: { default: { enabled: true, disabled: false } }
- class GlobalFilter < ScopeBooleanFilter; end
- filter GlobalFilter, arguments: { default: { global: true, specific: true } }
-
- field :id, as: :id, link_to_resource: true
-
- field :url, as: :text
- field :enabled?, as: :boolean
- field :failure_count, as: :number, sortable: true, index_text_align: :right
- field :user, as: :belongs_to
- field :rubygem, as: :belongs_to
- field :global?, as: :boolean
-
- field :hook_relay_stream, as: :text do
- stream_name = "webhook_id-#{model.id}"
- link_to stream_name, "https://app.hookrelay.dev/hooks/#{ENV['HOOK_RELAY_STREAM_ID']}?started_at=P1W&stream=#{stream_name}"
- end
-
- field :disabled_reason, as: :text
- field :disabled_at, as: :date_time, sortable: true
- field :last_success, as: :date_time, sortable: true
- field :last_failure, as: :date_time, sortable: true
- field :successes_since_last_failure, as: :number, sortable: true
- field :failures_since_last_success, as: :number, sortable: true
-
- tabs style: :pills do
- field :audits, as: :has_many
- end
-end
diff --git a/app/avo/resources/webauthn_credential.rb b/app/avo/resources/webauthn_credential.rb
new file mode 100644
index 00000000000..def6a1bcb20
--- /dev/null
+++ b/app/avo/resources/webauthn_credential.rb
@@ -0,0 +1,14 @@
+class Avo::Resources::WebauthnCredential < Avo::BaseResource
+ self.title = :id
+ self.includes = []
+
+ def fields
+ field :id, as: :id
+
+ field :external_id, as: :text
+ field :public_key, as: :text
+ field :nickname, as: :text
+ field :sign_count, as: :number
+ field :user, as: :belongs_to
+ end
+end
diff --git a/app/avo/resources/webauthn_credential_resource.rb b/app/avo/resources/webauthn_credential_resource.rb
deleted file mode 100644
index b6711b63eab..00000000000
--- a/app/avo/resources/webauthn_credential_resource.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-class WebauthnCredentialResource < Avo::BaseResource
- self.title = :id
- self.includes = []
-
- field :id, as: :id
- # Fields generated from the model
- field :external_id, as: :text
- field :public_key, as: :text
- field :nickname, as: :text
- field :sign_count, as: :number
- field :user, as: :belongs_to
- # add fields here
-end
diff --git a/app/avo/resources/webauthn_verification.rb b/app/avo/resources/webauthn_verification.rb
new file mode 100644
index 00000000000..48e509a1b76
--- /dev/null
+++ b/app/avo/resources/webauthn_verification.rb
@@ -0,0 +1,14 @@
+class Avo::Resources::WebauthnVerification < Avo::BaseResource
+ self.title = :id
+ self.includes = []
+
+ def fields
+ field :id, as: :id
+
+ field :path_token, as: :text
+ field :path_token_expires_at, as: :date_time
+ field :otp, as: :text
+ field :otp_expires_at, as: :date_time
+ field :user, as: :belongs_to
+ end
+end
diff --git a/app/avo/resources/webauthn_verification_resource.rb b/app/avo/resources/webauthn_verification_resource.rb
deleted file mode 100644
index 5834540ac89..00000000000
--- a/app/avo/resources/webauthn_verification_resource.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-class WebauthnVerificationResource < Avo::BaseResource
- self.title = :id
- self.includes = []
-
- field :id, as: :id
- # Fields generated from the model
- field :path_token, as: :text
- field :path_token_expires_at, as: :date_time
- field :otp, as: :text
- field :otp_expires_at, as: :date_time
- field :user, as: :belongs_to
- # add fields here
-end
diff --git a/config/initializers/avo.rb b/config/initializers/avo.rb
index 552309a8dca..fd05c1e42cc 100644
--- a/config/initializers/avo.rb
+++ b/config/initializers/avo.rb
@@ -118,7 +118,7 @@
Rails.configuration.to_prepare do
Avo::ApplicationController.include GitHubOAuthable
Avo::BaseController.prepend AvoAuditable
- Avo::BaseResource.include Concerns::AvoAuditableResource
+ Avo::BaseResource.include Avo::Resources::Concerns::AvoAuditableResource
Avo::ApplicationController.content_security_policy do |policy|
policy.style_src :self, "https://fonts.googleapis.com", :unsafe_inline