active_endpoint 0.2.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.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +55 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +60 -0
  5. data/.travis.yml +5 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +25 -0
  8. data/Gemfile.lock +140 -0
  9. data/LICENSE +21 -0
  10. data/README.md +233 -0
  11. data/Rakefile +6 -0
  12. data/active_endpoint.gemspec +28 -0
  13. data/app/assets/config/active_endpoint_manifest.js +2 -0
  14. data/app/assets/javascripts/active_endpoint/application.js +2 -0
  15. data/app/assets/stylesheets/active_endpoint/application.css +10 -0
  16. data/app/controllers/active_endpoint/application_controller.rb +5 -0
  17. data/app/controllers/active_endpoint/dashboard_controller.rb +7 -0
  18. data/app/controllers/active_endpoint/probes_controller.rb +22 -0
  19. data/app/controllers/active_endpoint/unregistred_probes_controller.rb +14 -0
  20. data/app/helpers/active_endpoint/application_helper.rb +105 -0
  21. data/app/models/active_endpoint/application_record.rb +5 -0
  22. data/app/models/active_endpoint/probe.rb +52 -0
  23. data/app/models/active_endpoint/unregistred_probe.rb +3 -0
  24. data/app/views/active_endpoint/application/_flashes.html.erb +5 -0
  25. data/app/views/active_endpoint/application/_header.html.erb +18 -0
  26. data/app/views/active_endpoint/dashboard/_blacklist.html.erb +57 -0
  27. data/app/views/active_endpoint/dashboard/_constraints.html.erb +117 -0
  28. data/app/views/active_endpoint/dashboard/_settings.html.erb +13 -0
  29. data/app/views/active_endpoint/dashboard/_tags.html.erb +20 -0
  30. data/app/views/active_endpoint/dashboard/index.html.erb +8 -0
  31. data/app/views/active_endpoint/probes/index.html.erb +28 -0
  32. data/app/views/active_endpoint/probes/show.html.erb +43 -0
  33. data/app/views/active_endpoint/probes/show_response.html.erb +9 -0
  34. data/app/views/active_endpoint/unregistred_probes/index.html.erb +28 -0
  35. data/app/views/layouts/active_endpoint/application.html.erb +39 -0
  36. data/bin/console +14 -0
  37. data/bin/rails +13 -0
  38. data/bin/setup +8 -0
  39. data/config/routes.rb +8 -0
  40. data/lib/active_endpoint.rb +60 -0
  41. data/lib/active_endpoint/concerns/configurable.rb +29 -0
  42. data/lib/active_endpoint/concerns/constraintable.rb +51 -0
  43. data/lib/active_endpoint/concerns/optionable.rb +41 -0
  44. data/lib/active_endpoint/concerns/rails_routable.rb +49 -0
  45. data/lib/active_endpoint/engine.rb +15 -0
  46. data/lib/active_endpoint/extentions/active_record.rb +30 -0
  47. data/lib/active_endpoint/logger.rb +28 -0
  48. data/lib/active_endpoint/proxy.rb +64 -0
  49. data/lib/active_endpoint/rails/middleware.rb +17 -0
  50. data/lib/active_endpoint/rails/railtie.rb +13 -0
  51. data/lib/active_endpoint/request.rb +79 -0
  52. data/lib/active_endpoint/response.rb +17 -0
  53. data/lib/active_endpoint/routes/blacklist.rb +67 -0
  54. data/lib/active_endpoint/routes/cache/proxy.rb +22 -0
  55. data/lib/active_endpoint/routes/cache/proxy/redis_store_proxy.rb +41 -0
  56. data/lib/active_endpoint/routes/cache/store.rb +73 -0
  57. data/lib/active_endpoint/routes/constraint_rule.rb +38 -0
  58. data/lib/active_endpoint/routes/constraints.rb +81 -0
  59. data/lib/active_endpoint/routes/matcher.rb +51 -0
  60. data/lib/active_endpoint/routes/momento.rb +81 -0
  61. data/lib/active_endpoint/storage.rb +112 -0
  62. data/lib/active_endpoint/tags.rb +15 -0
  63. data/lib/active_endpoint/version.rb +3 -0
  64. data/lib/generators/active_endpoint/install_generator.rb +35 -0
  65. data/lib/generators/templates/active_endpoint.rb +40 -0
  66. data/lib/generators/templates/migration.erb +30 -0
  67. metadata +109 -0
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'active_endpoint/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.version = ActiveEndpoint::VERSION
8
+
9
+ spec.name = 'active_endpoint'
10
+ spec.authors = ['Marat Khusnetdinov']
11
+ spec.email = ['marat@khusnetdinov.ru']
12
+
13
+ spec.summary = 'Your request tracking tool for rails applications'
14
+ spec.description = 'ActiveEndpoint is middleware for Rails applications that collects and analyses requests and responses per request for endpoint. It works with minimal impact on application\'s response time.'
15
+ spec.homepage = 'https://github.com/khusnetdinov/active_endpoint'
16
+ spec.license = 'MIT'
17
+
18
+ spec.homepage = 'https://github.com/khusnetdinov/active_endpoint'
19
+ spec.summary = 'Summary of ActiveEndpoint.'
20
+ spec.description = 'Description of ActiveEndpoint.'
21
+
22
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
23
+ f.match(%r{^(test|spec|features)/})
24
+ end
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ['lib']
28
+ end
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/active_endpoint .js
2
+ //= link_directory ../stylesheets/active_endpoint .css
@@ -0,0 +1,2 @@
1
+ //= require jquery
2
+ //= require jquery_ujs
@@ -0,0 +1,10 @@
1
+ /* = require_tree . */
2
+ /* = require_self */
3
+
4
+ .list-group-item > .badge {
5
+ background-color: auto !important;
6
+ }
7
+
8
+ .btn-sm {
9
+ padding: 0 9px !important;
10
+ }
@@ -0,0 +1,5 @@
1
+ module ActiveEndpoint
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ require_dependency 'active_endpoint/application_controller'
2
+
3
+ module ActiveEndpoint
4
+ class DashboardController < ApplicationController
5
+ def index; end
6
+ end
7
+ end
@@ -0,0 +1,22 @@
1
+ require_dependency 'active_endpoint/application_controller'
2
+
3
+ module ActiveEndpoint
4
+ class ProbesController < ApplicationController
5
+ def index
6
+ @probes_group = ActiveEndpoint::Probe.group_by_endpoint
7
+ end
8
+
9
+ def show
10
+ @probes = ActiveEndpoint::Probe.where(endpoint: params[:id])
11
+ end
12
+
13
+ def show_response
14
+ @probe = ActiveEndpoint::Probe.find(params[:probe_id])
15
+ end
16
+
17
+ def destroy
18
+ ActiveEndpoint::Probe.find(params[:id]).destroy
19
+ redirect_to :back
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ require_dependency 'active_endpoint/application_controller'
2
+
3
+ module ActiveEndpoint
4
+ class UnregistredProbesController < ApplicationController
5
+ def index
6
+ @probes = ActiveEndpoint::UnregistredProbe.all.select(:id, :uuid, :started_at, :path, :query_string)
7
+ end
8
+
9
+ def destroy
10
+ ActiveEndpoint::UnregistredProbe.find(params[:id]).destroy
11
+ redirect_to action: :index
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,105 @@
1
+ module ActiveEndpoint
2
+ module ApplicationHelper
3
+ SETTINGS = [
4
+ :cache_store_client,
5
+ :cache_prefix,
6
+ :constraint_limit,
7
+ :constraint_period,
8
+ :logger,
9
+ :log_probe_info,
10
+ :log_debug_info,
11
+ :storage_limit,
12
+ :storage_period,
13
+ :storage_keep_periods
14
+ ].freeze
15
+
16
+ TAGS_COLORS = [
17
+ 'label-info',
18
+ 'label-success',
19
+ 'label-warning',
20
+ 'label-danger',
21
+ 'label-default'
22
+ ].freeze
23
+
24
+ def settings
25
+ SETTINGS
26
+ end
27
+
28
+ def tags
29
+ tags = []
30
+ ActiveEndpoint.tags.definition.to_a.each_with_index do |definition, index|
31
+ tags << [
32
+ definition.first,
33
+ tag_label_by(index),
34
+ definition.last,
35
+ "ActiveEndpoint::Probe.tagged_as(:#{definition.first})"
36
+ ]
37
+ end
38
+ tags
39
+ end
40
+
41
+ def label_for(tag)
42
+ TAGS_COLORS[ActiveEndpoint.tags.definition.keys.index(tag.to_sym)]
43
+ end
44
+
45
+ def endpoints
46
+ ActiveEndpoint.blacklist.endpoints
47
+ end
48
+
49
+ def resources
50
+ ActiveEndpoint.blacklist.resources
51
+ end
52
+
53
+ def actions
54
+ ActiveEndpoint.blacklist.actions
55
+ end
56
+
57
+ def scopes
58
+ ActiveEndpoint.blacklist.scopes
59
+ end
60
+
61
+ def endpoints_constraints
62
+ ActiveEndpoint.constraints.endpoints.map do |endpoint, constraints|
63
+ constraints_for_html(endpoint, constraints)
64
+ end
65
+ end
66
+
67
+ def resources_constraints
68
+ ActiveEndpoint.constraints.resources.map do |endpoint, constraints|
69
+ constraints_for_html(endpoint, constraints)
70
+ end
71
+ end
72
+
73
+ def actions_constraints
74
+ ActiveEndpoint.constraints.actions.map do |endpoint, constraints|
75
+ constraints_for_html(endpoint, constraints)
76
+ end
77
+ end
78
+
79
+ def scopes_constraints
80
+ ActiveEndpoint.constraints.scopes.map do |endpoint, constraints|
81
+ constraints_for_html(endpoint, constraints)
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def tag_label_by(index)
88
+ if TAGS_COLORS.length > index
89
+ TAGS_COLORS[index]
90
+ else
91
+ TAGS_COLORS.last
92
+ end
93
+ end
94
+
95
+ def constraints_for_html(endpoint, constraints)
96
+ rule = constraints[:rule]
97
+ storage = constraints[:storage]
98
+
99
+ rules = "#{rule[:limit]} per #{distance_of_time_in_words(rule[:period])}"
100
+ storages = "#{storage[:limit]} per #{distance_of_time_in_words(storage[:period])}"
101
+
102
+ [endpoint, rules, storages]
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveEndpoint
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,52 @@
1
+ module ActiveEndpoint
2
+ class Probe < ApplicationRecord
3
+ validates :uuid, :started_at, :duration, :endpoint, :ip, :path,
4
+ :request_method, :url, presence: true
5
+
6
+ validates :finished_at, presence: true, unless: :skip_validation?
7
+
8
+ scope :unregistred, ->() { where(endpoint: :unregistred) }
9
+ scope :registred, ->() { where.not(endpoint: :unregistred) }
10
+ scope :probe, ->(endpoint) { where(endpoint: endpoint) }
11
+ scope :taken_before, ->(period) { where('created_at < ?', period) }
12
+ scope :group_by_endpoint, ->() {
13
+ execute_statement("
14
+ select count(*) as amount, endpoint, request_method as method, avg(duration) as duration
15
+ from active_endpoint_probes group by endpoint, request_method
16
+ having endpoint != 'unregistred'
17
+ ")
18
+ }
19
+
20
+ def tag
21
+ definitions = ActiveEndpoint.tags.definition
22
+ methods = ActiveEndpoint::Extentions::ActiveRecord::METHODS
23
+
24
+ definitions.each do |tag_name, operators|
25
+ last_operation_index = operators.length - 1
26
+
27
+ exec_operator = ''
28
+ operators.each_with_index do |(key, value), index|
29
+ exec_operator << "#{duration * 1000} #{methods[key]} #{value}"
30
+ exec_operator << (index == last_operation_index ? '' : ' && ')
31
+ end
32
+
33
+ return tag_name if eval(exec_operator)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def skip_validation?
40
+ type == 'ActiveEndpoint::UnregistredProbe'
41
+ end
42
+
43
+ def self.execute_statement(sql)
44
+ results = ActiveRecord::Base.connection.execute(sql)
45
+ if results.present?
46
+ return results.map(&:deep_symbolize_keys)
47
+ else
48
+ return nil
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveEndpoint
2
+ class UnregistredProbe < Probe; end
3
+ end
@@ -0,0 +1,5 @@
1
+ <% flash.each do |key, message| %>
2
+ <div class="alert alert-<%= key %>" style="border-radius: 0; margin: 0; text-align: center;" role="alert">
3
+ <%= message %>
4
+ </div>
5
+ <% end %>
@@ -0,0 +1,18 @@
1
+ <nav class="navbar-default navbar-toggleable-md navbar-light bg-faded">
2
+ <div class="container">
3
+ <button class="navbar-toggler navbar-toggler-right" style="top: 16px;" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
4
+ <span class="navbar-toggler-icon"></span>
5
+ </button>
6
+ <%= link_to 'ActiveEndpoint', active_endpoint.root_path, class: 'navbar-brand'%>
7
+ <div class="collapse navbar-collapse" id="navbarNav">
8
+ <ul class="nav navbar-nav">
9
+ <li class="<%= controller_name == 'probes' ? 'active' : '' %>">
10
+ <%= link_to 'Probes', active_endpoint.probes_path, class: '' %>
11
+ </li>
12
+ <li class="<%= controller_name == 'unregistred_probes' ? 'active' : '' %>">
13
+ <%= link_to 'Unregistred Probes', active_endpoint.unregistred_probes_path, class: '' %>
14
+ </li>
15
+ </ul>
16
+ </div>
17
+ </div>
18
+ </nav>
@@ -0,0 +1,57 @@
1
+ <div class="card">
2
+ <div class="card-header">Blacklist</div>
3
+ <ul class="list-group">
4
+ <li class="list-group-item">
5
+ <div class="row">
6
+ <div class="col">
7
+ <table class="table">
8
+ <thead class="thead-default">
9
+ <tr><th>Endpoints</th></tr>
10
+ </thead>
11
+ <tbody>
12
+ <%- endpoints.each do |endpoint| %>
13
+ <tr><td><%= endpoint %></td></tr>
14
+ <% end %>
15
+ </tbody>
16
+ </table>
17
+ </div>
18
+ <div class="col">
19
+ <table class="table">
20
+ <thead class="thead-default">
21
+ <tr><th>Resources</th></tr>
22
+ </thead>
23
+ <tbody>
24
+ <%- resources.each do |resource| %>
25
+ <tr><td><%= resource %></td></tr>
26
+ <% end %>
27
+ </tbody>
28
+ </table>
29
+ </div>
30
+ <div class="col">
31
+ <table class="table">
32
+ <thead class="thead-default">
33
+ <tr><th>Actions</th></tr>
34
+ </thead>
35
+ <tbody>
36
+ <%- actions.each do |actions| %>
37
+ <tr><td><%= actions %></td></tr>
38
+ <% end %>
39
+ </tbody>
40
+ </table>
41
+ </div>
42
+ <div class="col">
43
+ <table class="table">
44
+ <thead class="thead-default">
45
+ <tr><th>Scope</th></tr>
46
+ </thead>
47
+ <tbody>
48
+ <%- scopes.each do |scopes| %>
49
+ <tr><td><%= scopes %></td></tr>
50
+ <% end %>
51
+ </tbody>
52
+ </table>
53
+ </div>
54
+ </div>
55
+ </li>
56
+ </ul>
57
+ </div>
@@ -0,0 +1,117 @@
1
+ <div class="card">
2
+ <div class="card-header">Constraints</div>
3
+ <ul class="list-group">
4
+ <li class="list-group-item">
5
+ <div class="row">
6
+ <div class="col">
7
+ <table class="table">
8
+ <thead class="thead-default">
9
+ <tr>
10
+ <th>Endpoints</th>
11
+ <th>Request limits</th>
12
+ <th>Storage limits</th>
13
+ </tr>
14
+ </thead>
15
+ <tbody>
16
+ <%- if endpoints_constraints.any? %>
17
+ <%- endpoints_constraints.each do |endpoint, request, storage| %>
18
+ <tr>
19
+ <td style="width: 33%;"><%= endpoint %></td>
20
+ <td style="width: 33%;"><%= request %></td>
21
+ <td style="width: 33%;"><%= storage %></td>
22
+ </tr>
23
+ <% end %>
24
+ <% else %>
25
+ <tr><td colspan="3" style="text-align: center;">No Constraints</tr>
26
+ <% end %>
27
+ </tbody>
28
+ </table>
29
+ </div>
30
+ </div>
31
+ </li>
32
+ <li class="list-group-item">
33
+ <div class="row">
34
+ <div class="col">
35
+ <table class="table">
36
+ <thead class="thead-default">
37
+ <tr>
38
+ <th>Resources</th>
39
+ <th>Request limits</th>
40
+ <th>Storage limits</th>
41
+ </tr>
42
+ </thead>
43
+ <tbody>
44
+ <% if resources_constraints.any? %>
45
+ <%- resources_constraints.each do |endpoint, request, storage| %>
46
+ <tr>
47
+ <td style="width: 33%;"><%= endpoint %></td>
48
+ <td style="width: 33%;"><%= request %></td>
49
+ <td style="width: 33%;"><%= storage %></td>
50
+ </tr>
51
+ <% end %>
52
+ <% else %>
53
+ <tr><td colspan="3" style="text-align: center;">No Constraints</tr>
54
+ <% end %>
55
+ </tbody>
56
+ </table>
57
+ </div>
58
+ </div>
59
+ </li>
60
+ <li class="list-group-item">
61
+ <div class="row">
62
+ <div class="col">
63
+ <table class="table">
64
+ <thead class="thead-default">
65
+ <tr>
66
+ <th>Actions</th>
67
+ <th>Request limits</th>
68
+ <th>Storage limits</th>
69
+ </tr>
70
+ </thead>
71
+ <tbody>
72
+ <% if actions_constraints.any? %>
73
+ <%- actions_constraints.each do |endpoint, request, storage| %>
74
+ <tr>
75
+ <td style="width: 33%;"><%= endpoint %></td>
76
+ <td style="width: 33%;"><%= request %></td>
77
+ <td style="width: 33%;"> <%= storage %></td>
78
+ </tr>
79
+ <% end %>
80
+ <% else %>
81
+ <tr><td colspan="3" style="text-align: center;">No Constraints</tr>
82
+ <% end %>
83
+ </tbody>
84
+ </table>
85
+ </div>
86
+ </div>
87
+ </li>
88
+ <li class="list-group-item">
89
+ <div class="row">
90
+ <div class="col">
91
+ <table class="table">
92
+ <thead class="thead-default">
93
+ <tr>
94
+ <th>Scopes</th>
95
+ <th>Request limits</th>
96
+ <th>Storage limits</th>
97
+ </tr>
98
+ </thead>
99
+ <tbody>
100
+ <% if scopes_constraints.any? %>
101
+ <%- scopes_constraints.each do |endpoint, request, storage| %>
102
+ <tr>
103
+ <td style="width: 33%;"><%= endpoint %></td>
104
+ <td style="width: 33%;"><%= request %></td>
105
+ <td style="width: 33%;"><%= storage %></td>
106
+ </tr>
107
+ <% end %>
108
+ <% else %>
109
+ <tr><td colspan="3" style="text-align: center;">No Constraints</tr>
110
+ <% end %>
111
+ </tbody>
112
+ </table>
113
+ </div>
114
+ </div>
115
+ </li>
116
+ </ul>
117
+ </div>