snitch_reporting 1.0.7 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c842988f84c5f628e15f57cc36835b85fed9cec880b7d2fb2e6b137d9a74cbd
4
- data.tar.gz: 77290e2cf520feb8d711cf9f1d45df0a9f43d4b5ae93a071d1aba56e61c2fd42
3
+ metadata.gz: ea0cf12cf265e5dbdaeb502ce609cda532d5905041ec0f2ec96cde59cedeedcb
4
+ data.tar.gz: e22d3a0e011b516fd2f69ca487a674bcefbfbb5722940920c8f1e2cceb1108fa
5
5
  SHA512:
6
- metadata.gz: e21a8a1e211bdba81d0af8bdb98948d396013337a98c6c1a624318444634c0be43e767b4a0b7dbdc08892af50580662be3c5ed9c12a9bc68aa53da3dfafb2a6f
7
- data.tar.gz: 26b836607918d65d06c165422dded336808a8cf09bc154feded8a5985b64d844ed2878519ca5ffe8d1257a5c79c3d927be15b7c112539fdfa6a92eb883220dcb
6
+ metadata.gz: ed3dfb3a37fea761142366b66b1e3f2afa3e5bcb8fffb0227991fb0c2f4da3cdc3145b5cfa7e63d0ce42f13ee1e8c14c468b385ad8c856fa833e4e027345ac93
7
+ data.tar.gz: 909ba5d52e8585fd1e46944bf3a4d390d1e8d81035587d8194a6ba92dde0c2fb9b82bb13cb4565354aaff1259482a0f159e6370519c3e472a6f55511f5acdc70
@@ -0,0 +1,74 @@
1
+ document.addEventListener("DOMContentLoaded", function(){
2
+ document.querySelector("[name=filter_string]").addEventListener("focus", function(event) {
3
+ document.querySelector(".filters").classList.add("open")
4
+ })
5
+
6
+ setNewFilters(document.querySelector("[name=filter_string]").value)
7
+ });
8
+
9
+ document.addEventListener("click", function(event) {
10
+ if (event.target.matches(".filters") || event.target.matches("[name=filter_string]")) {
11
+ return event.stopPropagation()
12
+ }
13
+
14
+ if (event.target.matches(".filter-cell")) {
15
+ event.preventDefault()
16
+ return applyFilter(event.target)
17
+ }
18
+
19
+ document.querySelector(".filters").classList.remove("open")
20
+ })
21
+
22
+ function applyFilter(selected_cell) {
23
+ var filter_field = document.querySelector("[name=filter_string]")
24
+ var filter_string = filter_field.value
25
+
26
+ var matched_filters = filter_string.match(new RegExp(selected_cell.dataset.filterName + ":" + "\\w+"))
27
+ var current_filter = (matched_filters || [])[0]
28
+
29
+ if (current_filter == undefined) {
30
+ current_filter = selected_cell.dataset.filterName + ":" + selected_cell.dataset.filterValue
31
+
32
+ if (!filter_string.slice(-1) == " ") { filter_string = filter_string + " " }
33
+ filter_string = filter_string + current_filter
34
+ } else {
35
+ var filter_values = current_filter.split(":")
36
+ var filter_name = filter_values[0]
37
+ var filter_value = filter_values[1]
38
+ var filter_sort = filter_values[2]
39
+
40
+ var new_filter = current_filter.replace(filter_value, selected_cell.dataset.filterValue)
41
+
42
+ filter_string = filter_string.replace(current_filter, new_filter)
43
+ }
44
+
45
+ setNewFilters(filter_string)
46
+ }
47
+
48
+ function setNewFilters(filter_string) {
49
+ var filter_field = document.querySelector("[name=filter_string]")
50
+ filter_field.value = filter_string
51
+
52
+ var filters = {}, filter_strings = filter_string.match(/\w+\:\w+/g) || []
53
+ filter_strings.forEach(function(filter) {
54
+ var filter_values = filter.split(":")
55
+ var filter_name = filter_values[0]
56
+ var filter_value = filter_values[1]
57
+ var filter_sort = filter_values[2]
58
+
59
+ filters[filter_name] = filter_value
60
+ })
61
+
62
+ document.querySelectorAll(".filters .snitch-table").forEach(function(filter_table) {
63
+ var current_filter = filters[filter_table.dataset.filterName]
64
+ current_filter = current_filter || filter_table.dataset.default
65
+
66
+ filter_table.querySelectorAll(".filter-cell").forEach(function(filter_cell) {
67
+ if (filter_cell.dataset.filterValue == current_filter) {
68
+ filter_cell.classList.add("selected")
69
+ } else {
70
+ filter_cell.classList.remove("selected")
71
+ }
72
+ })
73
+ })
74
+ }
@@ -1,3 +1,5 @@
1
+ //= require_tree .
2
+
1
3
  document.addEventListener("change", function(evt) {
2
4
  if (evt.target && evt.target.hasAttribute("data-mark-resolution-url")) {
3
5
  var report_url = evt.target.getAttribute("data-mark-resolution-url")
@@ -78,6 +78,7 @@ todo {
78
78
  border-radius: 4px;
79
79
  background: $border-grey;
80
80
  padding: 3px 5px;
81
+ line-height: 2;
81
82
  white-space: nowrap;
82
83
  }
83
84
 
@@ -33,5 +33,54 @@
33
33
  }
34
34
  }
35
35
 
36
+ .snitch-search-area {
37
+ display: flex;
38
+ position: relative;
39
+ flex-direction: row-reverse;
40
+
41
+ .filters {
42
+ display: none;
43
+ position: absolute;
44
+ top: 60px;
45
+ right: 0;
46
+ left: 0;
47
+ z-index: 100;
48
+ transition: all 0.3s;
49
+ box-shadow: 2px 2px 4px $border-grey;
50
+ border: 3px solid $border-grey;
51
+ border-radius: 4px;
52
+ background: white;
53
+ padding: 20px;
54
+ }
55
+
56
+ input[name=filter_string] {
57
+ box-shadow: 2px 2px 4px $border-grey;
58
+ border: 2px solid $border-grey;
59
+ border-radius: 4px;
60
+ padding: 10px;
61
+ width: 100%;
62
+ font-size: 32px;
63
+ }
64
+
65
+ button[type=submit] {
66
+ cursor: pointer;
67
+ box-shadow: 2px 2px 4px $border-grey;
68
+ border: 1px solid $border-grey;
69
+ border-radius: 6px;
70
+ background: linear-gradient($snitch-blue, darken($snitch-blue, 15%));
71
+ padding: 0 12px;
72
+ color: white;
73
+ font-size: 18px;
74
+
75
+ &:hover {
76
+ background: linear-gradient(darken($snitch-blue, 15%), $snitch-blue, $snitch-blue);
77
+ }
78
+ }
79
+
80
+ .filters.open {
81
+ display: block;
82
+ }
83
+ }
84
+
36
85
  .snitch-breadcrumbs {}
37
86
  }
@@ -1 +1 @@
1
- @import "*";
1
+ @import "*";
@@ -14,6 +14,10 @@ class ::SnitchReporting::SnitchReportsController < ApplicationController
14
14
  # sort_reports
15
15
  end
16
16
 
17
+ def interpret_search
18
+ redirect_to snitch_reports_path(filter_string: params[:filter_string])
19
+ end
20
+
17
21
  def show
18
22
  @report = ::SnitchReporting::SnitchReport.find(params[:id])
19
23
  occurrences = @report.occurrences.order(created_at: :asc)
@@ -93,8 +97,8 @@ class ::SnitchReporting::SnitchReportsController < ApplicationController
93
97
  # end
94
98
  #
95
99
  # def set_report_preferences
96
- # @report_preferences = begin
97
- # preferences = JSON.parse(session[:report_preferences].presence || "{}").symbolize_keys
100
+ # @filters = begin
101
+ # preferences = JSON.parse(session[:filters].presence || "{}").symbolize_keys
98
102
  #
99
103
  # available_preferences = [:level_tags, :severity_tags, :source_tags, :resolved, :ignored]
100
104
  # available_preferences.each do |pref_key|
@@ -103,7 +107,7 @@ class ::SnitchReporting::SnitchReportsController < ApplicationController
103
107
  # preferences.delete(pref_key) if pref_val == "all"
104
108
  # end
105
109
  #
106
- # session[:report_preferences] = preferences.to_json
110
+ # session[:filters] = preferences.to_json
107
111
  # preferences
108
112
  # end
109
113
  # end
@@ -112,28 +116,30 @@ class ::SnitchReporting::SnitchReportsController < ApplicationController
112
116
  status: {
113
117
  default: :unresolved,
114
118
  values: [:all, :resolved, :unresolved]
115
- }
119
+ },
116
120
  # assignee: {
117
121
  # default: :any,
118
122
  # values: [:any, :me, :not_me, :not_assigned]
119
123
  # },
120
- # log_level: {
121
- # default: :any,
122
- # values: [:any] + ::SnitchReporting::SnitchReport.log_levels.keys.map(&:to_sym)
123
- # },
124
+ log_level: {
125
+ default: :any,
126
+ values: [:any] + ::SnitchReporting::SnitchReport.log_levels.keys.map(&:to_sym)
127
+ },
124
128
  # ignored: {
125
129
  # default: :not_ignored,
126
130
  # values: [:all, :ignored, :not_ignored]
127
131
  # }
128
132
  }
129
133
 
130
- @filters = @filter_sets.each_with_object({set_filters: {}}) do |(filter_name, filter_set), filters|
131
- filters[filter_name] = filter_set[:default]
132
- filter_in_param = params[filter_name].try(:to_sym)
133
- next unless filter_in_param && filter_set[:values].include?(filter_in_param)
134
- filters[filter_name] = filter_in_param
135
- filters[:set_filters][filter_name] = filter_in_param
136
- end
134
+ set_filter_string
135
+
136
+ # @filters = @filter_sets.each_with_object({set_filters: {}}) do |(filter_name, filter_set), filters|
137
+ # filters[filter_name] = filter_set[:default]
138
+ # filter_in_param = params[filter_name].try(:to_sym)
139
+ # next unless filter_in_param && filter_set[:values].include?(filter_in_param)
140
+ # filters[filter_name] = filter_in_param
141
+ # filters[:set_filters][filter_name] = filter_in_param
142
+ # end
137
143
  end
138
144
 
139
145
  def filter_reports
@@ -141,14 +147,87 @@ class ::SnitchReporting::SnitchReportsController < ApplicationController
141
147
 
142
148
  @reports = @reports.resolved if @filters[:status] == :resolved
143
149
  @reports = @reports.unresolved if @filters[:status] == :unresolved
144
- # @reports = @reports.search(@report_preferences[:by_fuzzy_text]) if @report_preferences[:by_fuzzy_text].present?
150
+ @reports = @reports.search(@filters[:search]) if @filters[:search].present?
145
151
  #
146
- # @reports = @reports.by_level(@report_preferences[:level_tags]) if @report_preferences[:level_tags].present?
147
- # @reports = @reports.by_severity(@report_preferences[:severity_tags]) if @report_preferences[:severity_tags].present?
148
- # @reports = @reports.by_source(@report_preferences[:source_tags]) if @report_preferences[:source_tags].present?
152
+ @reports = @reports.by_level(@filters[:log_level]) if @filters[:log_level].present?
153
+ # @reports = @reports.by_severity(@filters[:severity_tags]) if @filters[:severity_tags].present?
154
+ # @reports = @reports.by_source(@filters[:source_tags]) if @filters[:source_tags].present?
149
155
  #
150
- # @reports = @report_preferences[:resolved].present? && truthy?(@report_preferences[:resolved]) ? @reports.resolved : @reports.unresolved
151
- # @reports = @report_preferences[:ignored].present? && truthy?(@report_preferences[:ignored]) ? @reports.ignored : @reports.unignored
156
+ # @reports = @filters[:resolved].present? && truthy?(@filters[:resolved]) ? @reports.resolved : @reports.unresolved
157
+ # @reports = @filters[:ignored].present? && truthy?(@filters[:ignored]) ? @reports.ignored : @reports.unignored
158
+ end
159
+
160
+ def encode_string(token, str)
161
+ @interpolated_strings ||= []
162
+
163
+ @interpolated_strings << str
164
+ "#{token}(#{@interpolated_strings.length})"
165
+ end
166
+
167
+ def decode_string(token, encoded_str)
168
+ @interpolated_strings ||= []
169
+
170
+ encoded_str.gsub(/(\w+)\((\d+)\)/) do |found|
171
+ token = Regexp.last_match(1)
172
+ idx = Regexp.last_match(2)
173
+
174
+ if idx.to_i.to_s == idx
175
+ @interpolated_strings[idx.to_i - 1]
176
+ else
177
+ found
178
+ end
179
+ end
180
+ end
181
+
182
+ def param_safe_value(str)
183
+ return str unless str.include?(" ")
184
+
185
+ "\"#{str}\""
186
+ end
187
+
188
+ def set_filter_string
189
+ @filter_string = params[:filter_string] || session[:snitch_filter_string] || "status:unresolved"
190
+ @unknown_strings = []
191
+ @filters = {}
192
+
193
+ secret_key = loop do
194
+ token = SecureRandom.hex(10)
195
+ break token unless @filter_string.include?(token)
196
+ end
197
+
198
+ temp_filter_string = @filter_string.gsub(/\"(.*?)\"/) do |found|
199
+ encode_string(secret_key, Regexp.last_match(1))
200
+ end
201
+
202
+ search_strings = []
203
+ filter_strings = temp_filter_string.split(" ").select do |filter_string|
204
+ search_strings << filter_string unless filter_string.include?(":")
205
+ filter_string.include?(":")
206
+ end
207
+
208
+ filter_strings.each do |filter_string|
209
+ filter, value, * = filter_string.split(":")
210
+ if value.blank?
211
+ # Search pure text by itself
212
+ search_strings << value
213
+ elsif @filter_sets.keys.include?(filter.to_s.to_sym)
214
+ value = decode_string(secret_key, value)
215
+ @filters[filter.to_sym] = value.to_sym
216
+ elsif filter == "search"
217
+ search_strings << decode_string(secret_key, value)
218
+ else
219
+ @unknown_strings << filter_string
220
+ end
221
+ end
222
+
223
+ @filters[:search] = "\"#{search_strings.join(' ')}\"" if search_strings.any?
224
+
225
+ new_filter_strings = @filters.each_with_object([]) do |(filter_string, filter_value), filter_array|
226
+ filter_array << "#{filter_string}:#{filter_value}" if filter_value.present?
227
+ end
228
+
229
+ @filter_string = new_filter_strings.join(" ")
230
+ session[:snitch_filter_string] = @filter_string.dup
152
231
  end
153
232
  #
154
233
  # def sort_reports
@@ -55,10 +55,10 @@ class SnitchReporting::SnitchOccurrence < ApplicationRecord
55
55
  def backtrace=(trace_lines)
56
56
  already_traced = []
57
57
  self.backtrace_data = trace_lines.map do |trace_line|
58
- next unless trace_line.include?("/app/") # && trace_line.exclude?("app/models/snitch_reporting")
58
+ next unless trace_line.include?("/app/")
59
59
 
60
60
  joined_path = file_lines_from_backtrace(trace_line)
61
- next if joined_path.include?(joined_path)
61
+ next if already_traced.include?(joined_path)
62
62
  already_traced << joined_path
63
63
 
64
64
  file_path, line_number = joined_path.split(":", 2)
@@ -20,7 +20,6 @@ class SnitchReporting::SnitchReport < ApplicationRecord
20
20
  has_many :histories, class_name: "SnitchReporting::SnitchHistory", foreign_key: :report_id
21
21
 
22
22
  # belongs_to :assigned_to
23
- def assigned_to; end
24
23
  # belongs_to :resolved_by
25
24
  # belongs_to :ignored_by
26
25
 
@@ -31,6 +30,7 @@ class SnitchReporting::SnitchReport < ApplicationRecord
31
30
  scope :ignored, -> { where.not(ignored_at: nil) }
32
31
  scope :unignored, -> { where(ignored_at: nil) }
33
32
  scope :by_level, ->(*level_tags) { where(log_level: level_tags) }
33
+ scope :search, ->(text) { where("CONCAT(error, ' ', message) ILIKE :text", text: "%#{text}%") }
34
34
 
35
35
  enum log_level: {
36
36
  debug: 1,
@@ -271,7 +271,7 @@ class SnitchReporting::SnitchReport < ApplicationRecord
271
271
  end
272
272
 
273
273
  def tags=(new_tags)
274
- super((new_tags.try(:to_a) || [new_tags]).compact.flatten)
274
+ super((new_tags.try(:to_a) || [new_tags]).compact.flatten.uniq)
275
275
  end
276
276
 
277
277
  def add_tags(new_tags)
@@ -1,27 +1,26 @@
1
1
  <div class="snitch-reporting">
2
- <!--
3
- <div class="nav-tab">
4
- <input type="search" name="search" value="<%= params[:search] %>" placeholder="Search">
5
- </div>
6
- -->
7
- <div class="filters">
8
- <% @filter_sets.each do |filter_name, filter_set| %>
9
- <div class="snitch-table filter-table">
10
- <div class="snitch-tr">
11
- <div class="snitch-th"><%= filter_name.to_s.titleize %></div>
12
- </div>
13
2
 
14
- <% selected_filter_value = @filters[filter_name] %>
15
- <% default_filter_value = filter_set[:default] %>
16
- <% filter_set[:values].each do |filter_value| %>
17
- <div class="snitch-tr">
18
- <% url_value = filter_value == default_filter_value ? @filters[:set_filters].except(filter_name) : @filters[:set_filters].merge(filter_name => filter_value) %>
19
- <%= link_to filter_value.to_s.titleize, url_value, class: "snitch-td link-cell #{'selected' if selected_filter_value == filter_value}" %>
3
+ <%= form_tag interpret_search_path, class: "snitch-search-area" do %>
4
+ <button type="submit" name="button">Filter</button>
5
+ <input type="text" name="filter_string" value="<%= @filter_string %>">
6
+ <label for="search">
7
+ <div class="filters">
8
+ <% @filter_sets.each do |filter_name, filter_set| %>
9
+ <div class="snitch-table filter-table" data-filter-name="<%= filter_name %>" data-default="<%= filter_set[:default] %>">
10
+ <div class="snitch-tr">
11
+ <div class="snitch-th"><%= filter_name.to_s.titleize %></div>
12
+ </div>
13
+
14
+ <% filter_set[:values].each do |filter_value| %>
15
+ <div class="snitch-tr">
16
+ <%= link_to filter_value.to_s.titleize, {}, class: "snitch-td link-cell filter-cell", data: { filter_name: filter_name, filter_value: filter_value } %>
17
+ </div>
18
+ <% end %>
20
19
  </div>
21
20
  <% end %>
22
21
  </div>
23
- <% end %>
24
- </div>
22
+ </label>
23
+ <% end %>
25
24
 
26
25
  <div class="snitch-index">
27
26
  <div class="snitch-title-section">
@@ -63,7 +62,6 @@
63
62
  <span class="snitch-tag"><%= tag %></span><%= ', ' if idx < report.tags.length - 1 %>
64
63
  <% end %>
65
64
  </div>
66
- <!-- <div class="snitch-td"><%= report.assigned_to.try(:name).presence || "-" %></div> -->
67
65
  <div class="snitch-td">
68
66
  <%= content_tag :input, "", type: :checkbox, name: :resolved, class: "snitch-resolution-switch", checked: report.resolved?, data: { mark_resolution_url: snitch_report_url(report) } %>
69
67
  </div>
@@ -126,7 +126,7 @@
126
126
  <div id="params" class="snitch-section">
127
127
  <h3>Params</h3>
128
128
  <div class="line-trace">
129
- <span class="snitch-code"><%= JSON.pretty_generate(@occurrence.params) %></span>
129
+ <span class="snitch-code"><%= JSON.pretty_generate(@occurrence.params.presence || {}) %></span>
130
130
  </div>
131
131
  </div>
132
132
 
data/config/routes.rb CHANGED
@@ -2,4 +2,5 @@ SnitchReporting::Engine.routes.draw do
2
2
  root to: "snitch_reports#index"
3
3
 
4
4
  resources :snitch_reports, path: "/", only: [:index, :show, :update, :edit]
5
+ post :interpret_search, controller: :snitch_reports
5
6
  end
@@ -1,3 +1,3 @@
1
1
  module SnitchReporting
2
- VERSION = '1.0.7'
2
+ VERSION = '1.1.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snitch_reporting
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rocco Nicholls
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-27 00:00:00.000000000 Z
11
+ date: 2020-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -78,6 +78,7 @@ files:
78
78
  - README.md
79
79
  - Rakefile
80
80
  - app/assets/config/snitch_reporting_manifest.js
81
+ - app/assets/javascripts/snitch_reporting/search_bar.js
81
82
  - app/assets/javascripts/snitch_reporting/snitch_report.js
82
83
  - app/assets/stylesheets/snitch_reporting/_variables.scss
83
84
  - app/assets/stylesheets/snitch_reporting/components.scss