Skip to content

Commit

Permalink
feature: add search_result_url and search_policy_class (#1634)
Browse files Browse the repository at this point in the history
Co-authored-by: Adrian <[email protected]>
  • Loading branch information
glaucocustodio and adrianthedev authored Mar 21, 2023
1 parent 49a6558 commit c04ed9b
Show file tree
Hide file tree
Showing 19 changed files with 119 additions and 11 deletions.
11 changes: 11 additions & 0 deletions CONTRIBUTING.MD
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ gem 'avo', path: '../avo'

Avo's assets will not show up by default, resulting in 404 errors on `/avo-assets/avo.base.js` and `/avo-assets/avo.base.css`. To avoid this, you need to compile the asset bundles, and symlink them into `public/avo-assets`.

First, make sure you have `yarn` installed and then install Avo's dependencies:
```bash
yarn install
```

Run the first build to generate the files `app/assets/builds/avo.base.js` and `app/assets/builds/avo.base.css`:

```bash
yarn build
```

Create symlinks for compiled assets into the `public` directory. You'll only need to do this once.

```bash
Expand Down
2 changes: 1 addition & 1 deletion app/components/avo/views/resource_index_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<% end %>
<% c.body do %>
<div class="flex flex-col xs:flex-row xs:justify-between space-y-2 xs:space-y-0 py-4 <%= 'hidden' if @resource.search_query.nil? && @filters.empty? && available_view_types.count <= 1 %>">
<% unless hide_search_input %>
<% if show_search_input %>
<div class="flex items-center px-4 w-64">
<%= render partial: 'avo/partials/resource_search', locals: {resource: @resource.route_key, via_reflection: via_reflection} %>
</div>
Expand Down
15 changes: 12 additions & 3 deletions app/components/avo/views/resource_index_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,19 @@ def description
@resource.resource_description
end

def hide_search_input
return true unless @resource.search_query.present?
def show_search_input
return false unless authorized_to_search?
return false unless @resource.search_query.present?
return false if field&.hide_search_input

field&.hide_search_input || false
true
end

def authorized_to_search?
# Hide the search if the authorization prevents it
return true unless @resource.authorization.has_action_method?("search")

@resource.authorization.authorize_action("search", raise_exception: false)
end

private
Expand Down
11 changes: 9 additions & 2 deletions app/controllers/avo/search_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ def search_resources(resources)
resources
.map do |resource|
# Apply authorization
next unless @authorization.set_record(resource.model_class).authorize_action(:index, raise_exception: false)
next unless @authorization.set_record(resource.model_class).authorize_action(:search, raise_exception: false)

# Filter out the models without a search_query
next if resource.search_query.nil?

Expand Down Expand Up @@ -120,10 +121,16 @@ def apply_search_metadata(models, avo_resource)
models.map do |model|
resource = avo_resource.dup.hydrate(model: model).hydrate_fields(model: model)

record_path = if resource.search_result_path.present?
Avo::Hosts::ResourceRecordHost.new(block: resource.search_result_path, resource: resource, record: model).handle
else
resource.record_path
end

result = {
_id: model.id,
_label: resource.label,
_url: resource.record_path,
_url: record_path
}

if App.license.has_with_trial(:enhanced_search_results)
Expand Down
1 change: 1 addition & 0 deletions lib/avo/base_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class BaseResource
class_attribute :description, default: :id
class_attribute :search_query, default: nil
class_attribute :search_query_help, default: ""
class_attribute :search_result_path
class_attribute :includes, default: []
class_attribute :authorization_policy
class_attribute :translation_key
Expand Down
2 changes: 2 additions & 0 deletions lib/avo/hosts/base_host.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class BaseHost
option :params, default: proc { Avo::App.params }
option :view_context, default: proc { Avo::App.view_context }
option :current_user, default: proc { Avo::App.current_user }
option :main_app, default: proc { view_context.main_app }
option :avo, default: proc { view_context.avo }
# This is optional because we might instantiate the `Host` first and later hydrate it with a block.
option :block, optional: true

Expand Down
8 changes: 8 additions & 0 deletions lib/avo/services/authorization_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,16 @@ def defined_methods(model, **args)
end

def has_method?(method, **args)
method = "#{method}?" unless method.to_s.end_with? "?"
defined_methods(args[:record] || record, **args).include? method.to_sym
end

# Check the received method to see if the user overrode it in their config and then checks if it's present on the policy.
def has_action_method?(method, **args)
method = Avo.configuration.authorization_methods.stringify_keys[method.to_s] || method

has_method? method, **args
end
end
end
end
1 change: 1 addition & 0 deletions lib/generators/avo/templates/initializer/avo.tt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Avo.configure do |config|
# update: 'update?',
# create: 'create?',
# destroy: 'destroy?',
# search: 'search?',
# }
# config.raise_error_on_missing_policy = false
# config.authorization_client = :pundit
Expand Down
9 changes: 6 additions & 3 deletions spec/dummy/app/avo/resources/city_resource.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
class CityResource < Avo::BaseResource
self.title = :name
self.includes = []
# self.search_query = ->(params:) do
# scope.ransack(id_eq: params[:q], m: "or").result(distinct: false)
# end
self.search_query = -> do
scope.ransack(name_eq: params[:q]).result(distinct: false)
end
self.search_result_path = -> do
avo.resources_city_path record, custom: "yup"
end
self.extra_params = [:fish_type, :something_else, properties: [], information: [:name, :history]]

field :id, as: :id
Expand Down
4 changes: 4 additions & 0 deletions spec/dummy/app/policies/application_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ def update?
false
end

def avo_search?
false
end

def edit?
update?
end
Expand Down
4 changes: 4 additions & 0 deletions spec/dummy/app/policies/course_link_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ def act_on?
true
end

def avo_search?
true
end

def upload_attachments?
true
end
Expand Down
4 changes: 4 additions & 0 deletions spec/dummy/app/policies/course_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ def reorder_links?
false
end

def avo_search?
true
end

class Scope < Scope
def resolve
scope.all
Expand Down
4 changes: 4 additions & 0 deletions spec/dummy/app/policies/post_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ def act_on?
true
end

def avo_search?
true
end

def upload_attachments?
true
end
Expand Down
4 changes: 4 additions & 0 deletions spec/dummy/app/policies/team_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ def destroy?
true
end

def avo_search?
true
end

# Attachments
def upload_attachments?
true
Expand Down
4 changes: 4 additions & 0 deletions spec/dummy/app/policies/user_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ def act_on?
true
end

def avo_search?
true
end

def attach_post?
true
end
Expand Down
3 changes: 3 additions & 0 deletions spec/dummy/config/initializers/avo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
params: request.params
}
end
config.authorization_methods = {
search: "avo_search?" # override this method
}
# config.raise_error_on_missing_policy = true
# config.authorization_client = "Avo::Services::AuthorizationClients::ExtraPunditClient"

Expand Down
4 changes: 2 additions & 2 deletions spec/features/avo/hidden_from_global_search_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
let!(:team_membership) { team.team_members << user }

describe "global search" do
it "does not return the ream membership" do
it "does not return the team membership" do
get :index

expect(json['users']['count']).to eq 1
Expand All @@ -17,7 +17,7 @@
end

describe "resource search" do
it "does not return the ream membership" do
it "does not return the team membership" do
get :show, params: {
resource_name: 'memberships'
}
Expand Down
38 changes: 38 additions & 0 deletions spec/features/avo/search_with_custom_path.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require "rails_helper"

RSpec.feature "Search with custom path", type: :system do
let(:url) { "/admin/resources/cities" }

subject do
visit url
page
end

describe "global search" do
let!(:city) { create :city, name: "São Paulo" }

context "when search result path has been changed" do
it "can find the city" do
visit url
open_global_search_box
expect_search_panel_open

write_in_search "São Paulo"
expect(page).to have_content "São Paulo"
find(".aa-Panel").find(".aa-Item div", text: "São Paulo", match: :first).click
sleep 0.8

expect(page.current_url).to include("custom=yup")
end
end
end
end

def open_global_search_box
find(".global-search").click
end

def expect_search_panel_open
expect(page).to have_css ".aa-InputWrapper .aa-Input"
expect(page).to have_selector(".aa-Input:focus")
end
1 change: 1 addition & 0 deletions spec/rails_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ def driver_options(headless: false)
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
config.filter_run_when_matching :focus

config.before(:each, type: :system) { driven_by test_driver }

Expand Down

0 comments on commit c04ed9b

Please sign in to comment.