Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add /problems?exclude_apps=app1,app2 feature #1354

Merged
merged 4 commits into from Aug 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion .rubocop_todo.yml
Expand Up @@ -153,11 +153,13 @@ Metrics/AbcSize:
# ExcludedMethods: refine
Metrics/BlockLength:
Max: 418
Exclude:
- 'spec/**/*.rb'

# Offense count: 4
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 168
Max: 243

# Offense count: 6
Metrics/CyclomaticComplexity:
Expand Down
8 changes: 8 additions & 0 deletions app/controllers/problems_controller.rb
Expand Up @@ -23,14 +23,22 @@ class ProblemsController < ApplicationController
params[:all_errs]
end

expose(:filter) do
params[:filter]
end

expose(:params_environement) do
params[:environment]
end

# to use with_app_exclusions, hit a path like /problems?filter=-app:noisy_app%20-app:another_noisy_app
# it would be possible to add a really fancy UI for it at some point, but for now, it's really
# useful if there are noisy apps that you want to ignore.
expose(:problems) do
finder = Problem.
for_apps(app_scope).
in_env(params_environement).
filtered(filter).
all_else_unresolved(all_errs).
ordered_by(params_sort, params_order)

Expand Down
28 changes: 25 additions & 3 deletions app/models/problem.rb
Expand Up @@ -75,6 +75,30 @@ def self.in_env(env)
env.present? ? where(environment: env) : scoped
end

def self.filtered(filter)
if filter
filter_components = filter.scan(/(-app):(['"][^'"]+['"]|[^ ]+)/)
app_names_to_exclude = filter_components.map do |filter_component_tuple|
filter_type, filter_value = filter_component_tuple

# get rid of quotes that we pulled in from the regex matcher above
filter_value.gsub!(/^['"]/, '')
filter_value.gsub!(/['"]$/, '')

# this is the only supported filter_type at this time
if filter_type == '-app'
filter_value
end
end
end

if filter && app_names_to_exclude.present?
where(:app_name.nin => app_names_to_exclude)
else
scoped
end
end

def self.cache_notice(id, notice)
# increment notice count
message_digest = Digest::MD5.hexdigest(notice.message)
Expand Down Expand Up @@ -234,9 +258,7 @@ def zero_filled_grouped_noticed_counts(since, group_by = 'day')
buckets = group_by == 'day' ? 14 : 24

ruby_time_method = group_by == 'day' ? :yday : :hour
# rubocop:disable Performance/TimesMap
bucket_times = buckets.times.map { |ii| (since + ii.send(group_by)).send(ruby_time_method) }
# rubocop:enable Performance/TimesMap
bucket_times = Array.new(buckets) { |ii| (since + ii.send(group_by)).send(ruby_time_method) }
bucket_times.to_a.map do |bucket_time|
count = if (data_for_day = non_zero_filled.detect { |item| item.dig('_id', group_by) == bucket_time })
data_for_day['count']
Expand Down
12 changes: 12 additions & 0 deletions docs/apps.md
Expand Up @@ -11,3 +11,15 @@ for your Errbit app, and Errbit will ignore notices from older
application versions. Be sure your notifier is setting the
context.version field in its notifications (see
[https://airbrake.io/docs/](https://airbrake.io/docs/)).

## Excluding some apps when viewing problems (plus other awesome filtering in future)
Normally when you visit the /problems page, you see the most recently
received problems from all apps. Let's say you have a situation where
you have three apps in the system: awesomeapp, noisy_app, and
another_noisy_app. Further, let's assume that 99% of the problems
come from noisy_app and another_noisy_app, and that you don't care
about these apps for whatever reason. In this case, you could surf
to the problems page and exclude the noisy apps from view like this:
/problems?filter=-app:noisy_app%20-app:another_noisy_app

There is no UI for this feature, just the query param.
41 changes: 39 additions & 2 deletions spec/models/problem_spec.rb
@@ -1,4 +1,3 @@
# rubocop:disable Metrics/BlockLength
describe Problem, type: 'model' do
context 'validations' do
it 'requires an environment' do
Expand Down Expand Up @@ -272,6 +271,45 @@
end
end

context "filtered" do
before do
@app1 = Fabricate(:app)
@problem1 = Fabricate(:problem, app: @app1)

@app2 = Fabricate(:app)
@problem2 = Fabricate(:problem, app: @app2)

@app3 = Fabricate(:app)
@app3.update_attribute(:name, 'app3')

@problem3 = Fabricate(:problem, app: @app3)
end

it "#filtered returns problems but excludes those attached to the specified apps" do
expect(Problem.filtered("-app:'#{@app1.name}'")).to include(@problem2)
expect(Problem.filtered("-app:'#{@app1.name}'")).to_not include(@problem1)

filtered_results_with_two_exclusions = Problem.filtered("-app:'#{@app1.name}' -app:app3")
expect(filtered_results_with_two_exclusions).to_not include(@problem1)
expect(filtered_results_with_two_exclusions).to include(@problem2)
expect(filtered_results_with_two_exclusions).to_not include(@problem3)
end

it "#filtered does not explode if given a nil filter" do
filtered_results = Problem.filtered(nil)
expect(filtered_results).to include(@problem1)
expect(filtered_results).to include(@problem2)
expect(filtered_results).to include(@problem3)
end

it "#filtered does nothing for unimplemented filter types" do
filtered_results = Problem.filtered("filterthatdoesnotexist:hotapp")
expect(filtered_results).to include(@problem1)
expect(filtered_results).to include(@problem2)
expect(filtered_results).to include(@problem3)
end
end

context "#app_name" do
let!(:app) { Fabricate(:app) }
let!(:problem) { Fabricate(:problem, app: app) }
Expand Down Expand Up @@ -555,4 +593,3 @@
end
end
end
# rubocop:enable Metrics/BlockLength