├── .gitignore
├── .rspec
├── .rubocop.yml
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── README.md
├── Rakefile
├── active_endpoint.gemspec
├── app
├── assets
│ ├── config
│ │ └── active_endpoint_manifest.js
│ ├── javascripts
│ │ └── active_endpoint
│ │ │ └── application.js
│ └── stylesheets
│ │ └── active_endpoint
│ │ └── application.css
├── controllers
│ └── active_endpoint
│ │ ├── application_controller.rb
│ │ ├── dashboard_controller.rb
│ │ ├── probes_controller.rb
│ │ └── unregistred_probes_controller.rb
├── helpers
│ └── active_endpoint
│ │ └── application_helper.rb
├── models
│ └── active_endpoint
│ │ ├── application_record.rb
│ │ ├── probe.rb
│ │ └── unregistred_probe.rb
└── views
│ ├── active_endpoint
│ ├── application
│ │ ├── _flashes.html.erb
│ │ └── _header.html.erb
│ ├── dashboard
│ │ ├── _blacklist.html.erb
│ │ ├── _constraints.html.erb
│ │ ├── _settings.html.erb
│ │ ├── _tags.html.erb
│ │ └── index.html.erb
│ ├── probes
│ │ ├── index.html.erb
│ │ ├── show.html.erb
│ │ └── show_response.html.erb
│ └── unregistred_probes
│ │ └── index.html.erb
│ └── layouts
│ └── active_endpoint
│ └── application.html.erb
├── bin
├── console
├── rails
└── setup
├── config
└── routes.rb
├── lib
├── active_endpoint.rb
├── active_endpoint
│ ├── concerns
│ │ ├── configurable.rb
│ │ ├── constraintable.rb
│ │ ├── optionable.rb
│ │ ├── rails_routable.rb
│ │ └── settingable.rb
│ ├── engine.rb
│ ├── extentions
│ │ ├── active_record.rb
│ │ └── active_support.rb
│ ├── logger.rb
│ ├── proxy.rb
│ ├── rails
│ │ ├── middleware.rb
│ │ └── railtie.rb
│ ├── request.rb
│ ├── response.rb
│ ├── routes
│ │ ├── blacklist.rb
│ │ ├── cache
│ │ │ ├── proxy.rb
│ │ │ ├── proxy
│ │ │ │ └── redis_store_proxy.rb
│ │ │ └── store.rb
│ │ ├── constraint_rule.rb
│ │ ├── constraints.rb
│ │ ├── matcher.rb
│ │ └── momento.rb
│ ├── storage.rb
│ ├── tags.rb
│ └── version.rb
└── generators
│ ├── active_endpoint
│ └── install_generator.rb
│ └── templates
│ ├── active_endpoint.rb
│ └── migration.erb
└── spec
├── active_endpoint
├── concerns
│ ├── configurabe_spec.rb
│ ├── constrainable_spec.rb
│ ├── optionable_spec.rb
│ ├── rails_routable_spec.rb
│ └── settingable_spec.rb
├── extentions
│ └── active_record_spec.rb
├── logger_spec.rb
├── proxy_spec.rb
├── request_spec.rb
├── response_spec.rb
├── routes
│ ├── blacklist_spec.rb
│ ├── cache
│ │ ├── proxy
│ │ │ └── redis_store_proxy_spec.rb
│ │ ├── proxy_spec.rb
│ │ └── store_spec.rb
│ ├── constraint_rule_spec.rb
│ ├── constraints_spec.rb
│ ├── matcher_spec.rb
│ └── momento_spec.rb
├── storage_spec.rb
└── tags_spec.rb
└── spec_helper.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | /.config
4 | /coverage/
5 | /InstalledFiles
6 | /pkg/
7 | /spec/reports/
8 | /spec/examples.txt
9 | /test/tmp/
10 | /test/version_tmp/
11 | /tmp/
12 |
13 | # Used by dotenv library to load environment variables.
14 | # .env
15 |
16 | ## Specific to RubyMotion:
17 | .dat*
18 | .repl_history
19 | build/
20 | *.bridgesupport
21 | build-iPhoneOS/
22 | build-iPhoneSimulator/
23 |
24 | ## Specific to RubyMotion (use of CocoaPods):
25 | #
26 | # We recommend against adding the Pods directory to your .gitignore. However
27 | # you should judge for yourself, the pros and cons are mentioned at:
28 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29 | #
30 | # vendor/Pods/
31 |
32 | ## Documentation cache and generated files:
33 | /.yardoc/
34 | /_yardoc/
35 | /doc/
36 | /rdoc/
37 |
38 | ## Environment normalization:
39 | /.bundle/
40 | /vendor/bundle
41 | /lib/bundler/man/
42 |
43 | # for a library or gem, you might want to ignore these files since the code is
44 | # intended to run in multiple environments; otherwise, check them in:
45 | # Gemfile.lock
46 | # .ruby-version
47 | # .ruby-gemset
48 |
49 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50 | .rvmrc
51 |
52 | # other
53 | /.rspec_status
54 | /.byebug_history
55 | /dump.rdb
56 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --format documentation
2 | --color
3 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | Encoding:
2 | Enabled: false
3 |
4 | LineLength:
5 | Max: 120
6 |
7 | MethodLength:
8 | Max: 25
9 |
10 | AbcSize:
11 | Max: 30
12 |
13 | Documentation:
14 | Enabled: false
15 |
16 | IndentationConsistency:
17 | Enabled: false
18 |
19 | ModuleFunction:
20 | Enabled: false
21 |
22 | RegexpLiteral:
23 | MaxSlashes: 0
24 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: ruby
3 | rvm:
4 | - 2.1.9
5 | - 2.2.5
6 | - 2.3.1
7 | - 2.3.4
8 | - 2.4.1
9 |
10 | before_install: gem install bundler
11 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at marat@khusnetdinov.ru. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gemspec
4 |
5 | gem 'sass-rails'
6 |
7 | group :development do
8 | gem 'bundler'
9 | gem 'reek'
10 | gem 'rspec'
11 | gem 'rubocop'
12 | gem 'pry'
13 | end
14 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | active_endpoint (0.2.0)
5 | rack
6 |
7 | GEM
8 | remote: https://rubygems.org/
9 | specs:
10 | actionpack (4.1.16)
11 | actionview (= 4.1.16)
12 | activesupport (= 4.1.16)
13 | rack (~> 1.5.2)
14 | rack-test (~> 0.6.2)
15 | actionview (4.1.16)
16 | activesupport (= 4.1.16)
17 | builder (~> 3.1)
18 | erubis (~> 2.7.0)
19 | activesupport (4.1.16)
20 | i18n (~> 0.6, >= 0.6.9)
21 | json (~> 1.7, >= 1.7.7)
22 | minitest (~> 5.1)
23 | thread_safe (~> 0.1)
24 | tzinfo (~> 1.1)
25 | ast (2.3.0)
26 | axiom-types (0.1.1)
27 | descendants_tracker (~> 0.0.4)
28 | ice_nine (~> 0.11.0)
29 | thread_safe (~> 0.3, >= 0.3.1)
30 | builder (3.2.3)
31 | codeclimate-engine-rb (0.4.0)
32 | virtus (~> 1.0)
33 | coderay (1.1.1)
34 | coercible (1.0.0)
35 | descendants_tracker (~> 0.0.1)
36 | concurrent-ruby (1.0.5)
37 | descendants_tracker (0.0.4)
38 | thread_safe (~> 0.3, >= 0.3.1)
39 | diff-lcs (1.3)
40 | equalizer (0.0.11)
41 | erubis (2.7.0)
42 | ffi (1.9.18)
43 | i18n (0.8.6)
44 | ice_nine (0.11.2)
45 | json (1.8.6)
46 | method_source (0.8.2)
47 | minitest (5.10.3)
48 | parallel (1.12.0)
49 | parser (2.4.0.0)
50 | ast (~> 2.2)
51 | powerpack (0.1.1)
52 | pry (0.10.4)
53 | coderay (~> 1.1.0)
54 | method_source (~> 0.8.1)
55 | slop (~> 3.4)
56 | rack (1.5.5)
57 | rack-test (0.6.3)
58 | rack (>= 1.0)
59 | railties (4.1.16)
60 | actionpack (= 4.1.16)
61 | activesupport (= 4.1.16)
62 | rake (>= 0.8.7)
63 | thor (>= 0.18.1, < 2.0)
64 | rainbow (2.2.2)
65 | rake
66 | rake (10.5.0)
67 | rb-fsevent (0.10.2)
68 | rb-inotify (0.9.10)
69 | ffi (>= 0.5.0, < 2)
70 | redis (3.3.3)
71 | redis-activesupport (5.0.3)
72 | activesupport (>= 3, < 6)
73 | redis-store (~> 1.3.0)
74 | redis-store (1.3.0)
75 | redis (>= 2.2)
76 | reek (4.7.2)
77 | codeclimate-engine-rb (~> 0.4.0)
78 | parser (>= 2.4.0.0, < 2.5)
79 | rainbow (~> 2.0)
80 | rspec (3.6.0)
81 | rspec-core (~> 3.6.0)
82 | rspec-expectations (~> 3.6.0)
83 | rspec-mocks (~> 3.6.0)
84 | rspec-core (3.6.0)
85 | rspec-support (~> 3.6.0)
86 | rspec-expectations (3.6.0)
87 | diff-lcs (>= 1.2.0, < 2.0)
88 | rspec-support (~> 3.6.0)
89 | rspec-mocks (3.6.0)
90 | diff-lcs (>= 1.2.0, < 2.0)
91 | rspec-support (~> 3.6.0)
92 | rspec-support (3.6.0)
93 | rubocop (0.49.1)
94 | parallel (~> 1.10)
95 | parser (>= 2.3.3.1, < 3.0)
96 | powerpack (~> 0.1)
97 | rainbow (>= 1.99.1, < 3.0)
98 | ruby-progressbar (~> 1.7)
99 | unicode-display_width (~> 1.0, >= 1.0.1)
100 | ruby-progressbar (1.8.1)
101 | sass (3.5.1)
102 | sass-listen (~> 4.0.0)
103 | sass-listen (4.0.0)
104 | rb-fsevent (~> 0.9, >= 0.9.4)
105 | rb-inotify (~> 0.9, >= 0.9.7)
106 | sass-rails (5.0.6)
107 | railties (>= 4.0.0, < 6)
108 | sass (~> 3.1)
109 | sprockets (>= 2.8, < 4.0)
110 | sprockets-rails (>= 2.0, < 4.0)
111 | tilt (>= 1.1, < 3)
112 | slop (3.6.0)
113 | sprockets (3.7.1)
114 | concurrent-ruby (~> 1.0)
115 | rack (> 1, < 3)
116 | sprockets-rails (3.2.0)
117 | actionpack (>= 4.0)
118 | activesupport (>= 4.0)
119 | sprockets (>= 3.0.0)
120 | thor (0.19.4)
121 | thread_safe (0.3.6)
122 | tilt (2.0.8)
123 | tzinfo (1.2.3)
124 | thread_safe (~> 0.1)
125 | unicode-display_width (1.3.0)
126 | virtus (1.0.5)
127 | axiom-types (~> 0.1)
128 | coercible (~> 1.0)
129 | descendants_tracker (~> 0.0, >= 0.0.3)
130 | equalizer (~> 0.0, >= 0.0.9)
131 |
132 | PLATFORMS
133 | ruby
134 |
135 | DEPENDENCIES
136 | actionpack (>= 3.0.0)
137 | active_endpoint!
138 | activesupport (>= 3.0.0)
139 | bundler
140 | pry
141 | rake
142 | redis-activesupport
143 | reek
144 | rspec
145 | rubocop
146 | sass-rails
147 |
148 | BUNDLED WITH
149 | 1.14.3
150 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Marat Khusnetdinov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ActiveEndpoint [](https://travis-ci.org/khusnetdinov/active_endpoint) [](https://badge.fury.io/rb/active_endpoint) [](https://www.codetriage.com/khusnetdinov/active_endpoint)
2 |
3 | ## Your request tracking tool for rails applications
4 |
5 | ## Attention! Gem in under active test and is preparing for first release !
6 |
7 | 
8 |
9 |
10 | ## Usage
11 |
12 | 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.
13 |
14 | This gem uses `ActiveSupport::Notifications` and `Cache Storage` to reduce possible impact on application request / response processing time.
15 |
16 | ## Features
17 |
18 | - Metrics are stored in database for further tracking and analysis.
19 | - History rotation with configurable limits of records amount and age.
20 | - Routes filter (blacklist).
21 | - Probes tagging by processing time.
22 |
23 | ## Metrics
24 |
25 | These endpoint metrics are stored in DB:
26 |
27 | - `:uuid` - uniq probe identifier
28 | - `:endpoint` - requested endpoint
29 | - `:path` - requested full path
30 | - `:query_string` - request query string
31 | - `:request_method` - http request method
32 | - `:ip` - ip address asked request
33 | - `:url` - request full url
34 | - `:xhr` - is request ajax?
35 | - `:started_at` - probe start time
36 | - `:finished_at` - probe finish time
37 | - `:duration` - probe request duration
38 | - `:params` - parsed requested params
39 | - `:response` - Base64 encoded html response
40 | - `:body` - Base64 encoded request body
41 |
42 | Additional information is taken from Rack log:
43 |
44 | `:base_url, :content_charset, :content_length, :content_type, :fullpath, :http_version, :http_connection, :http_accept_encoding, :http_accept_language, :media_type, :media_type_params, :method, :path_info, :pattern, :port, :protocol, :server_name, :ssl`.
45 |
46 | Requests which are not recognized by rails router are stored as `unregistred`.
47 |
48 | ## Requirements
49 |
50 | - `redis` as cache storage
51 |
52 | Be sure that you have all requrements installed on you machine.
53 |
54 | ## Installation
55 |
56 | Add this line to your application's Gemfile:
57 |
58 | ```ruby
59 | gem 'active_endpoint'
60 | ```
61 |
62 | And then execute:
63 |
64 | $ bundle
65 |
66 | Or install it yourself as:
67 |
68 | $ gem install active_endpoint
69 |
70 | Setup project for using gem:
71 |
72 | $ rails generate active_endpoint:install
73 |
74 | Migrate database for models:
75 |
76 | $ rake db:migrate # Rails <=4
77 | $ rails db:migrate # Rails >=5
78 |
79 | Now project has all files and settings that allow you to use gem.
80 |
81 | ## Configuration
82 |
83 | ### Endpoints filter (blacklist)
84 |
85 | By default ActiveEndpoint treats all routes as `whitelist` routes. To filter some endpoints you can use `blackilist` configuration, as shown below:
86 |
87 | ```ruby
88 | ActiveEndpoint.configure do |endpoint|
89 | endpoint.blacklist.configure do |blacklist|
90 | # Ignore endpoint "welcome#index"
91 | blacklist.add(endpoint: "welcome#index")
92 |
93 | # Ignore "web/users" controller actions
94 | blacklist.add(resources: ["web/users"])
95 |
96 | # Ignore "web/users#show" action with scoped controller
97 | blacklist.add(scope: "web", resources: "users", actions: ["show"])
98 |
99 | # Ignore "admin" scope controllers
100 | blacklist.add(scope: "admin")
101 | end
102 | end
103 | ```
104 |
105 | #### Ignore one endpoint
106 |
107 | `blacklist.add(endpoint: "users#index")` - Ignore one endpoint.
108 |
109 | #### Ignore controller actions
110 |
111 | `blacklist.add(resources: "users")` - Ignore all actions for `UsersController`.
112 |
113 | `blacklist.add(resources: ["users", "managers"])` - Ignore all actions in `UsersController` and `ManagersController`.
114 |
115 | `blacklist.add(resources: "users", actions: ["show"])` - Ignore only `show` action in `UsersController`.
116 |
117 | #### Ignore namespace or scope
118 |
119 | `blacklist.add(scope: "admin")` - Ignore all controllers and actions for `admin` namespace or scope.
120 |
121 | ### Constraints
122 |
123 | You can specify the amount and period of request records to keep in database. Records which exceed these limits are automatically removed from database.
124 | See example below:
125 |
126 | ```ruby
127 | ActiveEndpoint.configure do |endpoint|
128 | # Defines default settings, 1 probe per 10 minutes for endpoint request
129 | constraint_limit = 1
130 | constraint_period = 10.minutes
131 |
132 | endpoint.constraints.configure do |constraints|
133 | # Constraint endpoint "welcome#index" with 1 minute period and default limit
134 | # and configure database constraints to keep 1000 probes per 1 week.
135 | constraints.add(endpoint: "welcome#index",
136 | rule: { 1.minute },
137 | storage: { limit: 1000, period: 1.week })
138 | # Constraints "web/users" controller actions with custom limit and period
139 | # with defailt storage constraints
140 | constraints.add(resources: ["web/users"], rule: { limit: 100, period: 5.minutes })
141 | end
142 | end
143 | ```
144 | NOTE: To define a constraint you should define at least one limit or period.
145 |
146 | ### Storage settings
147 |
148 | ActiveEndpoint creates two models in you rails application: `Probe` and it's child `UnregistredProbe`.
149 | To prevent problems with database probes are removed when user defines custom period. Also you can limit storage probes in database.
150 | It is recommended to define own storage default to prevent unwanted probes deletion. See example below:
151 |
152 | ```ruby
153 | ActiveEndpoint.configure do |endpoint|
154 | # Define default limit for maximum probes amount
155 | endpoint.storage_limit = 1000
156 |
157 | # Define default period to keep probes in database.
158 | endpoint.storage_period = 1.week
159 |
160 | # Define amount of periods (constraint periods) that endpoints are kept in database.
161 | endpoint.storage_keep_periods = 2
162 | end
163 | ```
164 |
165 | ### Tagging probes
166 | You can group probes by tags automatically assigned according to request processing time (ms). See example below:
167 |
168 | ```ruby
169 | ActiveEndpoint.configure do |endpoint|
170 | endpoint.tags.configure do |tags|
171 | tags.add(:fast, { less_than: 250 })
172 | tags.add(:normal, { greater_than_or_equal_to: 250, less_than: 500 })
173 | tags.add(:slow, { greater_than_or_equal_to: 500, less_than: 750 })
174 | tags.add(:acceptable, { greater_than_or_equal_to: 500, less_than: 1000 })
175 | tags.add(:need_optimization, { greater_than_or_equal_to: 1000 })
176 | end
177 | end
178 | ```
179 |
180 | #### Mehods for conditions
181 |
182 | - greater_than = '>'
183 | - greater_than_or_equal_to = '>=',
184 | - equal_to = '=',
185 | - less_than = '<',
186 | - less_than_or_equal_to = '<=',
187 |
188 | #### Tagged model scopes
189 |
190 | Defined tags are also usefull for scopes queries:
191 |
192 | ```ruby
193 | ActiveEndpoint::Probe.tagged_as(:need_optimization)
194 | #=> Returns all probes having corresponding tag and thus matching the condition
195 | # { greater_than_or_equal_to: 1000 }
196 | ```
197 |
198 | #### Instance methods
199 |
200 | Check tag on model:
201 |
202 | ```ruby
203 | ActiveEndpoint::Probe.last.tag
204 | #=> Returns probe's tag
205 | ```
206 |
207 | ### Web UI
208 |
209 | ActiveEndpoint offer rails engine for managing probes. Mount it:
210 |
211 | ```ruby
212 | mount ActiveEndpoint::Engine => '/active_endpoint'
213 | ```
214 |
215 | 
216 |
217 | ## License
218 |
219 | This gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
220 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'bundler/gem_tasks'
2 | require 'rspec/core/rake_task'
3 |
4 | RSpec::Core::RakeTask.new(:spec)
5 |
6 | task default: :spec
7 |
--------------------------------------------------------------------------------
/active_endpoint.gemspec:
--------------------------------------------------------------------------------
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 | spec.test_files = Dir.glob('spec/**/*')
29 |
30 | spec.required_ruby_version = '>= 2.0.0'
31 |
32 | spec.add_dependency 'rack'
33 |
34 | spec.add_development_dependency 'rake'
35 | spec.add_development_dependency 'activesupport', '>= 3.0.0'
36 | spec.add_development_dependency 'actionpack', '>= 3.0.0'
37 | spec.add_development_dependency 'redis-activesupport'
38 | end
39 |
--------------------------------------------------------------------------------
/app/assets/config/active_endpoint_manifest.js:
--------------------------------------------------------------------------------
1 | //= link_directory ../javascripts/active_endpoint .js
2 | //= link_directory ../stylesheets/active_endpoint .css
3 |
--------------------------------------------------------------------------------
/app/assets/javascripts/active_endpoint/application.js:
--------------------------------------------------------------------------------
1 | //= require jquery
2 | //= require jquery_ujs
3 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/active_endpoint/application.css:
--------------------------------------------------------------------------------
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 | }
--------------------------------------------------------------------------------
/app/controllers/active_endpoint/application_controller.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | class ApplicationController < ActionController::Base
3 | protect_from_forgery with: :exception
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/controllers/active_endpoint/dashboard_controller.rb:
--------------------------------------------------------------------------------
1 | require_dependency 'active_endpoint/application_controller'
2 |
3 | module ActiveEndpoint
4 | class DashboardController < ApplicationController
5 | def index; end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/controllers/active_endpoint/probes_controller.rb:
--------------------------------------------------------------------------------
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
23 |
--------------------------------------------------------------------------------
/app/controllers/active_endpoint/unregistred_probes_controller.rb:
--------------------------------------------------------------------------------
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
15 |
--------------------------------------------------------------------------------
/app/helpers/active_endpoint/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | module ApplicationHelper
3 | SETTINGS = [
4 | :cache_store_client,
5 | :cache_prefix,
6 | :constraint_limit,
7 | :constraint_period,
8 | :logger,
9 | :storage_limit,
10 | :storage_period,
11 | :storage_keep_periods
12 | ].freeze
13 |
14 | TAGS_COLORS = [
15 | 'label-info',
16 | 'label-success',
17 | 'label-warning',
18 | 'label-danger',
19 | 'label-default'
20 | ].freeze
21 |
22 | def settings
23 | SETTINGS
24 | end
25 |
26 | def tags
27 | tags = []
28 | ActiveEndpoint.tags.definition.to_a.each_with_index do |definition, index|
29 | tags << [
30 | definition.first,
31 | tag_label_by(index),
32 | definition.last,
33 | "ActiveEndpoint::Probe.tagged_as(:#{definition.first})"
34 | ]
35 | end
36 | tags
37 | end
38 |
39 | def label_for(tag)
40 | TAGS_COLORS[ActiveEndpoint.tags.definition.keys.index(tag.to_sym)]
41 | end
42 |
43 | def endpoints_constraints
44 | ActiveEndpoint.constraints.endpoints.map do |endpoint, constraints|
45 | constraints_for_html(endpoint, constraints)
46 | end
47 | end
48 |
49 | def resources_constraints
50 | ActiveEndpoint.constraints.resources.map do |endpoint, constraints|
51 | constraints_for_html(endpoint, constraints)
52 | end
53 | end
54 |
55 | def actions_constraints
56 | ActiveEndpoint.constraints.actions.map do |endpoint, constraints|
57 | constraints_for_html(endpoint, constraints)
58 | end
59 | end
60 |
61 | def scopes_constraints
62 | ActiveEndpoint.constraints.scopes.map do |endpoint, constraints|
63 | constraints_for_html(endpoint, constraints)
64 | end
65 | end
66 |
67 | private
68 |
69 | def tag_label_by(index)
70 | if TAGS_COLORS.length > index
71 | TAGS_COLORS[index]
72 | else
73 | TAGS_COLORS.last
74 | end
75 | end
76 |
77 | def constraints_for_html(endpoint, constraints)
78 | rule = constraints[:rule]
79 | storage = constraints[:storage]
80 |
81 | rules = "#{rule[:limit]} per #{distance_of_time_in_words(rule[:period])}"
82 | storages = "#{storage[:limit]} per #{distance_of_time_in_words(storage[:period])}"
83 |
84 | [endpoint, rules, storages]
85 | end
86 | end
87 | end
88 |
--------------------------------------------------------------------------------
/app/models/active_endpoint/application_record.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | class ApplicationRecord < ActiveRecord::Base
3 | self.abstract_class = true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/models/active_endpoint/probe.rb:
--------------------------------------------------------------------------------
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 |
13 | def tag
14 | methods = ActiveEndpoint::Extentions::ActiveRecord::METHODS
15 |
16 | ActiveEndpoint.tags.definition.each do |tag_name, operators|
17 | last_operation_index = operators.length - 1
18 |
19 | exec_operator = ''
20 | operators.each_with_index do |(key, value), index|
21 | exec_operator << "#{duration * 1000} #{methods[key]} #{value}"
22 | exec_operator << (index == last_operation_index ? '' : ' && ')
23 | end
24 |
25 | return tag_name if eval(exec_operator)
26 | end
27 | end
28 |
29 | private
30 |
31 | def skip_validation?
32 | type == 'ActiveEndpoint::UnregistredProbe'
33 | end
34 |
35 | def self.group_by_endpoint
36 | sql = <<-sql
37 | select count(*) as amount, endpoint, request_method as method, avg(duration) as duration
38 | from active_endpoint_probes group by endpoint, request_method
39 | having endpoint != 'unregistred'
40 | sql
41 |
42 | results = ActiveRecord::Base.find_by_sql(sql)
43 |
44 | if results.present?
45 | return results.map(&:deep_symbolize_keys)
46 | else
47 | return nil
48 | end
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/app/models/active_endpoint/unregistred_probe.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | class UnregistredProbe < Probe; end
3 | end
4 |
--------------------------------------------------------------------------------
/app/views/active_endpoint/application/_flashes.html.erb:
--------------------------------------------------------------------------------
1 | <% flash.each do |key, message| %>
2 |
3 | <%= message %>
4 |
5 | <% end %>
--------------------------------------------------------------------------------
/app/views/active_endpoint/application/_header.html.erb:
--------------------------------------------------------------------------------
1 |
19 |
--------------------------------------------------------------------------------
/app/views/active_endpoint/dashboard/_blacklist.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 | Endpoints |
10 |
11 |
12 | <%- ActiveEndpoint.blacklist.endpoints.each do |endpoint| %>
13 | <%= endpoint %> |
14 | <% end %>
15 |
16 |
17 |
18 |
19 |
20 |
21 | Resources |
22 |
23 |
24 | <%- ActiveEndpoint.blacklist.resources.each do |resource| %>
25 | <%= resource %> |
26 | <% end %>
27 |
28 |
29 |
30 |
31 |
32 |
33 | Actions |
34 |
35 |
36 | <%- ActiveEndpoint.blacklist.actions.each do |actions| %>
37 | <%= actions %> |
38 | <% end %>
39 |
40 |
41 |
42 |
43 |
44 |
45 | Scope |
46 |
47 |
48 | <%- ActiveEndpoint.blacklist.scopes.each do |scopes| %>
49 | <%= scopes %> |
50 | <% end %>
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/app/views/active_endpoint/dashboard/_constraints.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 | Endpoints |
11 | Request limits |
12 | Storage limits |
13 |
14 |
15 |
16 | <%- if endpoints_constraints.any? %>
17 | <%- endpoints_constraints.each do |endpoint, request, storage| %>
18 |
19 | <%= endpoint %> |
20 | <%= request %> |
21 | <%= storage %> |
22 |
23 | <% end %>
24 | <% else %>
25 | No Constraints |
26 | <% end %>
27 |
28 |
29 |
30 |
31 |
32 | -
33 |
34 |
35 |
36 |
37 |
38 | Resources |
39 | Request limits |
40 | Storage limits |
41 |
42 |
43 |
44 | <% if resources_constraints.any? %>
45 | <%- resources_constraints.each do |endpoint, request, storage| %>
46 |
47 | <%= endpoint %> |
48 | <%= request %> |
49 | <%= storage %> |
50 |
51 | <% end %>
52 | <% else %>
53 | No Constraints |
54 | <% end %>
55 |
56 |
57 |
58 |
59 |
60 | -
61 |
62 |
63 |
64 |
65 |
66 | Actions |
67 | Request limits |
68 | Storage limits |
69 |
70 |
71 |
72 | <% if actions_constraints.any? %>
73 | <%- actions_constraints.each do |endpoint, request, storage| %>
74 |
75 | <%= endpoint %> |
76 | <%= request %> |
77 | <%= storage %> |
78 |
79 | <% end %>
80 | <% else %>
81 | No Constraints |
82 | <% end %>
83 |
84 |
85 |
86 |
87 |
88 | -
89 |
90 |
91 |
92 |
93 |
94 | Scopes |
95 | Request limits |
96 | Storage limits |
97 |
98 |
99 |
100 | <% if scopes_constraints.any? %>
101 | <%- scopes_constraints.each do |endpoint, request, storage| %>
102 |
103 | <%= endpoint %> |
104 | <%= request %> |
105 | <%= storage %> |
106 |
107 | <% end %>
108 | <% else %>
109 | No Constraints |
110 | <% end %>
111 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/app/views/active_endpoint/dashboard/_settings.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%- settings.each do |setting|%>
5 | -
6 |
7 |
<%= setting.to_s.gsub('_', ' ').capitalize %>
8 |
<%= ActiveEndpoint.get_settings(setting) %>
9 |
10 |
11 | <% end %>
12 |
13 |
--------------------------------------------------------------------------------
/app/views/active_endpoint/dashboard/_tags.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%- tags.each do |name, label_class, condition, helper| %>
5 | -
6 |
7 |
8 | <%= name %>
9 |
10 |
11 | <%= condition %>
12 |
13 |
14 | <%= helper %>
15 |
16 |
17 |
18 | <% end %>
19 |
20 |
--------------------------------------------------------------------------------
/app/views/active_endpoint/dashboard/index.html.erb:
--------------------------------------------------------------------------------
1 | Dashboard
2 | <%= render 'settings' %>
3 |
4 | <%= render 'tags' %>
5 |
6 | <%= render 'blacklist' %>
7 |
8 | <%= render 'constraints' %>
--------------------------------------------------------------------------------
/app/views/active_endpoint/probes/index.html.erb:
--------------------------------------------------------------------------------
1 | Probes
2 |
3 |
4 |
5 |
6 | endpoint |
7 | method |
8 | amount in period |
9 | Average duration |
10 | |
11 |
12 |
13 |
14 | <% if @probes_group.any?%>
15 | <% @probes_group.each do |group| %>
16 |
17 | <%= group[:endpoint] %> |
18 | <%= group[:method] %> |
19 | <%= group[:amount] %> |
20 | <%= (group[:duration].round(3) * 1000).to_i %> |
21 | <%= link_to 'Show', active_endpoint.probe_path(id: group[:endpoint]), class: 'btn btn-info btn-sm' %> |
22 |
23 | <% end %>
24 | <% else %>
25 | No Probes |
26 | <% end %>
27 |
28 |
--------------------------------------------------------------------------------
/app/views/active_endpoint/probes/show.html.erb:
--------------------------------------------------------------------------------
1 | Probes for <%= @endpoint %>
2 |
3 |
4 |
5 |
6 | uuid |
7 | ip |
8 | xhr? |
9 | query string |
10 | params |
11 | duration |
12 | tag |
13 | created at |
14 | |
15 |
16 |
17 |
18 | <%- if @probes.any? %>
19 | <%- @probes.each do |probe| %>
20 |
21 | <%= probe.uuid %> |
22 | <%= probe.ip %> |
23 | <%= probe.xhr %> |
24 | <%= probe.query_string %> |
25 | <%= probe.params %> |
26 | <%= (probe.duration * 1000).to_i %> |
27 |
28 |
29 | <%= probe.tag %>
30 |
31 | |
32 | <%= probe.created_at %> |
33 |
34 | <%= link_to 'Show Response', active_endpoint.probe_show_response_path(probe), class: 'btn btn-info btn-sm' %>
35 | <%= link_to 'Remove', active_endpoint.probe_path(probe), method: :delete, class: 'btn btn-danger btn-sm' %>
36 | |
37 |
38 | <% end%>
39 | <% else %>
40 | No Probes |
41 | <% end %>
42 |
43 |
--------------------------------------------------------------------------------
/app/views/active_endpoint/probes/show_response.html.erb:
--------------------------------------------------------------------------------
1 | Proberesponse for <%= @probe.endpoint %> with <%= @probe.uuid %>
2 |
3 |
4 | <%= Base64.decode64(@probe.response) %>
5 |
6 |
7 |
8 |
9 | <%= link_to 'Back', :back, class: '' %>
10 |
--------------------------------------------------------------------------------
/app/views/active_endpoint/unregistred_probes/index.html.erb:
--------------------------------------------------------------------------------
1 | Unregistred Probes
2 |
3 |
4 |
5 |
6 | uuid |
7 | path |
8 | query string |
9 | started_at |
10 | |
11 |
12 |
13 |
14 | <% if @probes.any?%>
15 | <% @probes.each do |probe| %>
16 |
17 | <%= probe.uuid %> |
18 | <%= probe.path %> |
19 | <%= probe.query_string%> |
20 | <%= probe.started_at %> |
21 | <%= link_to 'Remove', active_endpoint.unregistred_probe_path(probe.id), method: :delete, class: 'btn btn-danger btn-sm' %> |
22 |
23 | <% end%>
24 | <% else %>
25 | No Probes |
26 | <% end %>
27 |
28 |
--------------------------------------------------------------------------------
/app/views/layouts/active_endpoint/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ActiveEndpoint
8 |
9 |
10 |
11 |
12 |
13 | <%= stylesheet_link_tag 'active_endpoint/application', media: 'all' %>
14 | <%= csrf_meta_tags %>
15 |
16 |
17 |
18 |
21 |
24 |
25 |
26 | <%= yield %>
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | <%= javascript_include_tag 'active_endpoint/application' %>
38 |
39 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'bundler/setup'
4 | require 'active_endpoint'
5 |
6 | # You can add fixtures and/or initialization code here to make experimenting
7 | # with your gem easier. You can also use a different console, if you like.
8 |
9 | # (If you use this, don't forget to add pry to your Gemfile!)
10 | # require "pry"
11 | # Pry.start
12 |
13 | require 'irb'
14 | IRB.start(__FILE__)
15 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails gems
3 | # installed from the root of your application.
4 |
5 | ENGINE_ROOT = File.expand_path('../..', __FILE__)
6 | ENGINE_PATH = File.expand_path('../../lib/active_endpoint/engine', __FILE__)
7 |
8 | # Set up gems listed in the Gemfile.
9 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
10 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
11 |
12 | require 'rails/all'
13 | require 'rails/engine/commands'
14 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 | set -vx
5 |
6 | bundle install
7 |
8 | # Do any other automated setup that you need to do here
9 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | ActiveEndpoint::Engine.routes.draw do
2 | root 'dashboard#index'
3 |
4 | resources :probes, only: [:index, :show, :destroy] do
5 | get :show_response
6 | end
7 | resources :unregistred_probes, only: [:index, :destroy]
8 | end
9 |
--------------------------------------------------------------------------------
/lib/active_endpoint.rb:
--------------------------------------------------------------------------------
1 | require 'base64'
2 | require 'rack'
3 | require 'rack/request'
4 | require 'active_support/time'
5 | require 'active_support/rails'
6 | require 'active_support/core_ext/module/delegation'
7 |
8 | require 'active_endpoint/extentions/active_support'
9 | require 'active_endpoint/concerns/settingable'
10 | require 'active_endpoint/concerns/configurable'
11 | require 'active_endpoint/concerns/constraintable'
12 | require 'active_endpoint/concerns/optionable'
13 | require 'active_endpoint/concerns/rails_routable'
14 | require 'active_endpoint/extentions/active_record'
15 | require 'active_endpoint/routes/momento'
16 | require 'active_endpoint/routes/blacklist'
17 | require 'active_endpoint/routes/constraints'
18 | require 'active_endpoint/routes/constraint_rule'
19 | require 'active_endpoint/routes/matcher'
20 | require 'active_endpoint/routes/cache/proxy/redis_store_proxy'
21 | require 'active_endpoint/routes/cache/proxy'
22 | require 'active_endpoint/routes/cache/store'
23 | require 'active_endpoint/proxy'
24 | require 'active_endpoint/logger'
25 | require 'active_endpoint/request'
26 | require 'active_endpoint/response'
27 | require 'active_endpoint/storage'
28 | require 'active_endpoint/tags'
29 | require 'active_endpoint/version'
30 |
31 | module ActiveEndpoint
32 | extend Settingable
33 | extend Configurable
34 |
35 | define_setting :blacklist, ActiveEndpoint::Routes::Blacklist.new
36 |
37 | define_setting :cache_store_client, :redis
38 | define_setting :cache_prefix, 'active_endpoint'
39 |
40 | define_setting :constraint_limit, 10
41 | define_setting :constraint_period, 10.minutes
42 | define_setting :constraints, ActiveEndpoint::Routes::Constraints.new
43 |
44 | define_setting :logger, ActiveEndpoint::Logger
45 |
46 | define_setting :storage_limit, 1000
47 | define_setting :storage_period, 1.day
48 | define_setting :storage_keep_periods, 2
49 |
50 | define_setting :tags, ActiveEndpoint::Tags.new
51 | end
52 |
53 | if defined?(::Rails)
54 | require 'rails/generators'
55 | require 'rails/generators/migration'
56 |
57 | require 'active_endpoint/rails/middleware'
58 | require 'active_endpoint/rails/railtie'
59 | require 'active_endpoint/engine'
60 | end
61 |
--------------------------------------------------------------------------------
/lib/active_endpoint/concerns/configurable.rb:
--------------------------------------------------------------------------------
1 | module Configurable
2 | def configure
3 | yield self
4 | end
5 | end
--------------------------------------------------------------------------------
/lib/active_endpoint/concerns/constraintable.rb:
--------------------------------------------------------------------------------
1 | module Constraintable
2 | def constraints(options)
3 | {
4 | rule: rule_constraints(options),
5 | storage: storage_constraints(options)
6 | }
7 | end
8 |
9 | def rule_constraints(options)
10 | rule_options = fetch_rule(options)
11 |
12 | defined_rule_constraints = {
13 | limit: fetch_limit(rule_options),
14 | period: fetch_period(rule_options)
15 | }.reject { |_key, value| value.nil? }
16 |
17 | default_rule_constraints.merge(defined_rule_constraints)
18 | end
19 |
20 | def storage_constraints(options)
21 | storage_options = fetch_storage(options)
22 |
23 | defined_storage_constraints = {
24 | limit: fetch_limit(storage_options),
25 | period: fetch_period(storage_options)
26 | }.reject { |_key, value| value.nil? }
27 |
28 | default_storage_constraints.merge(defined_storage_constraints)
29 | end
30 |
31 | def default_constraints
32 | {
33 | rule: default_rule_constraints,
34 | storage: default_storage_constraints
35 | }
36 | end
37 |
38 | def default_rule_constraints
39 | {
40 | limit: ActiveEndpoint.constraint_limit,
41 | period: ActiveEndpoint.constraint_period
42 | }
43 | end
44 |
45 | def default_storage_constraints
46 | {
47 | limit: ActiveEndpoint.storage_limit,
48 | period: ActiveEndpoint.storage_period
49 | }
50 | end
51 | end
--------------------------------------------------------------------------------
/lib/active_endpoint/concerns/optionable.rb:
--------------------------------------------------------------------------------
1 | module Optionable
2 | def fetch_endpoint(options)
3 | return nil unless options
4 | options[:endpoint]
5 | end
6 |
7 | def fetch_actions(options)
8 | return nil unless options
9 | options[:actions]
10 | end
11 |
12 | def fetch_resources(options)
13 | return nil unless options
14 | options[:resources]
15 | end
16 |
17 | def fetch_scope(options)
18 | return nil unless options
19 | options[:scope]
20 | end
21 |
22 | def fetch_limit(options)
23 | return nil unless options
24 | options[:limit]
25 | end
26 |
27 | def fetch_period(options)
28 | return nil unless options
29 | options[:period]
30 | end
31 |
32 | def fetch_storage(options)
33 | return nil unless options
34 | options[:storage]
35 | end
36 |
37 | def fetch_rule(options)
38 | return nil unless options
39 | options[:rule]
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/lib/active_endpoint/concerns/rails_routable.rb:
--------------------------------------------------------------------------------
1 | module RailsRoutable
2 | ACTION_KEYS = [:controller, :action].freeze
3 |
4 | def rails_action?(request)
5 | rails_action(request).present?
6 | end
7 |
8 | def rails_request_params(request)
9 | action = rails_action(request)
10 | return unless action
11 | action.reject do |key, _value|
12 | ACTION_KEYS.include?(key)
13 | end
14 | end
15 |
16 | def rails_endpoint(request)
17 | action = rails_action(request)
18 | return unless action
19 | action.select do |key, _value|
20 | ACTION_KEYS.include?(key)
21 | end
22 | end
23 |
24 | def rails_action(request)
25 | rails_routes.recognize_path(request.path, method: request.method)
26 | rescue ActionController::RoutingError
27 | nil
28 | end
29 |
30 | def rails_route_pattern(request)
31 | rails_routes.router.recognize(request) do |route|
32 | return route.path.spec.to_s
33 | end
34 | rescue ActionController::RoutingError
35 | nil
36 | end
37 |
38 | private
39 |
40 | def rails_routes
41 | ::Rails.application.routes
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/lib/active_endpoint/concerns/settingable.rb:
--------------------------------------------------------------------------------
1 | module Settingable
2 | def define_setting(name, default = nil)
3 | class_variable_set("@@#{name}", default)
4 |
5 | define_class_method "#{name}=" do |value|
6 | class_variable_set("@@#{name}", value)
7 | end
8 |
9 | define_class_method name do
10 | class_variable_get("@@#{name}")
11 | end
12 | end
13 |
14 | def get_settings(name)
15 | class_variable_get("@@#{name}")
16 | end
17 |
18 | private
19 |
20 | def define_class_method(name, &block)
21 | (class << self; self; end).instance_eval do
22 | define_method name, &block
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/lib/active_endpoint/engine.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | class Engine < ::Rails::Engine
3 | isolate_namespace ActiveEndpoint
4 |
5 | initializer 'active_endpoint.assets.precompile' do |app|
6 | app.config.assets.precompile += %w[]
7 | end
8 |
9 | config.generators do |g|
10 | g.orm :active_record
11 | g.template_engine :erb
12 | g.test_framework :rspec
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/active_endpoint/extentions/active_record.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | module Extentions
3 | module ActiveRecord
4 | METHODS = {
5 | greater_than: '>',
6 | greater_than_or_equal_to: '>=',
7 | equal_to: '=',
8 | less_than: '<',
9 | less_than_or_equal_to: '<='
10 | }.freeze
11 |
12 | def tagged_as(tag, tags = ActiveEndpoint.tags.definition)
13 | return [] unless tags.keys.include?(tag)
14 |
15 | time_operators = tags[tag]
16 | last_operator_index = time_operators.keys.length - 1
17 |
18 | query = ''
19 | time_operators.each_with_index do |(key, value), index|
20 | operator = METHODS[key]
21 | duration = (value.to_f / 1000).to_s
22 | and_operator = last_operator_index == index ? '' : ' AND '
23 | query << 'duration ' + operator + ' ' + duration + ' ' + and_operator
24 | end
25 |
26 | where(query)
27 | end
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/lib/active_endpoint/extentions/active_support.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | module Extentions
3 | module ActiveSupport
4 | class ::Numeric
5 | def days
6 | ::ActiveSupport::Duration.new(24.hours * self, [[:days, self]])
7 | end
8 | alias :day :days
9 |
10 | def weeks
11 | ::ActiveSupport::Duration.new(7.days * self, [[:days, self * 7]])
12 | end
13 | alias :week :weeks
14 |
15 | def fortnights
16 | ::ActiveSupport::Duration.new(2.weeks * self, [[:days, self * 14]])
17 | end
18 | alias :fortnight :fortnights
19 | end
20 |
21 | class ::Integer
22 | def months
23 | ::ActiveSupport::Duration.new(30.days * self, [[:months, self]])
24 | end
25 | alias :month :months
26 |
27 | def years
28 | ::ActiveSupport::Duration.new(365.25.days * self, [[:years, self]])
29 | end
30 | alias :year :years
31 | end
32 | end
33 | end
34 | end
--------------------------------------------------------------------------------
/lib/active_endpoint/logger.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | class Logger
3 | class << self
4 | def info(caller, info)
5 | logger.info(message(caller, info))
6 | end
7 |
8 | def debug(caller, message)
9 | logger.debug(message(caller, message))
10 | end
11 |
12 | def error(caller, error)
13 | logger.error(message(caller, error))
14 | end
15 |
16 | private
17 |
18 | def logger
19 | ::Rails.logger
20 | end
21 |
22 | def message(caller, message)
23 | "ActiveEndpoint::Logger [#{caller}] - #{message}"
24 | end
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/active_endpoint/proxy.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | class Proxy
3 | def initialize(matcher = ActiveEndpoint::Routes::Matcher.new)
4 | @matcher = matcher
5 | @created_at = Time.now
6 | end
7 |
8 | def track(env, &block)
9 | request = ActiveEndpoint::Request.new(env)
10 |
11 | ActiveEndpoint.logger.debug('ActiveEndpoint::Blacklist', ActiveEndpoint.blacklist)
12 | ActiveEndpoint.logger.debug('ActiveEndpoint::Constraints', ActiveEndpoint.constraints)
13 |
14 | if @matcher.whitelisted?(request)
15 | track_begin(request)
16 | status, headers, response = yield block
17 | track_end(response)
18 | [status, headers, response]
19 | else
20 | register(request) if @matcher.unregistred?(request)
21 |
22 | yield block
23 | end
24 | rescue => error
25 | @logger.error(self.class, error)
26 |
27 | yield block
28 | end
29 |
30 | private
31 |
32 | def track_begin(request)
33 | @request = request.probe
34 | end
35 |
36 | def track_end(response, finished_at = Time.now)
37 | @response = ActiveEndpoint::Response.new(response).probe
38 | @finished_at = finished_at
39 |
40 | probe = {
41 | created_at: @created_at,
42 | finished_at: @finished_at,
43 | request: @request,
44 | response: @response
45 | }
46 |
47 | ActiveSupport::Notifications.instrument('active_endpoint.tracked_probe', probe: probe) if @matcher.allow_account?(@request)
48 | end
49 |
50 | def register(request)
51 | unregistred = {
52 | created_at: @created_at,
53 | finished_at: @finished_at,
54 | request: request.probe
55 | }
56 |
57 | ActiveSupport::Notifications.instrument('active_endpoint.unregistred_probe', probe: unregistred) if @matcher.allow_register?(request)
58 | end
59 | end
60 | end
61 |
--------------------------------------------------------------------------------
/lib/active_endpoint/rails/middleware.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | module Rails
3 | class Middleware
4 | def initialize(app)
5 | @app = app
6 | end
7 |
8 | def call(env)
9 | dup._call(env, ActiveEndpoint::Proxy.new)
10 | end
11 |
12 | private
13 |
14 | def _call(env, proxy)
15 | proxy.track(env) { @app.call(env) }
16 | end
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/active_endpoint/rails/railtie.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | module Rails
3 | class Railtie < ::Rails::Railtie
4 | initializer 'active_endpoint.configure_rails_initialization' do |app|
5 | ActiveSupport.on_load(:active_record) do
6 | ActiveRecord::Base.send(:extend, ::ActiveEndpoint::Extentions::ActiveRecord)
7 | end
8 |
9 | app.middleware.insert(0, ActiveEndpoint::Rails::Middleware)
10 | end
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/active_endpoint/request.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | class Request < Rack::Request
3 | include RailsRoutable
4 |
5 | def probe
6 | {
7 | base_url: base_url,
8 | body: body,
9 | content_charset: content_charset,
10 | content_length: content_length,
11 | content_type: content_type,
12 | endpoint: endpoint,
13 | fullpath: fullpath,
14 | http_version: http_version,
15 | http_connection: http_connection,
16 | http_accept_encoding: http_accept_encoding,
17 | http_accept_language: http_accept_language,
18 | ip: ip,
19 | media_type: media_type,
20 | media_type_params: media_type_params,
21 | method: method,
22 | params: params,
23 | path: path,
24 | path_info: path_info,
25 | pattern: pattern,
26 | port: port,
27 | protocol: protocol,
28 | query_string: query_string,
29 | request_method: request_method,
30 | server_name: server_name,
31 | ssl: ssl?,
32 | url: url,
33 | xhr: xhr?
34 | }
35 | end
36 |
37 | private
38 |
39 | def method
40 | request_method.downcase.to_sym
41 | end
42 |
43 | def endpoint
44 | action = rails_endpoint(self)
45 | return unless action
46 | "#{action[:controller]}##{action[:action]}"
47 | end
48 |
49 | def http_accept_encoding
50 | get_header('HTTP_ACCEPT_ENCODING')
51 | end
52 |
53 | def http_accept_language
54 | get_header('HTTP_ACCEPT_LANGUAGE')
55 | end
56 |
57 | def http_connection
58 | get_header('HTTP_CONNECTION')
59 | end
60 |
61 | def http_version
62 | get_header('HTTP_VERSION')
63 | end
64 |
65 | def params
66 | rails_request_params(self)
67 | end
68 |
69 | def pattern
70 | rails_route_pattern(self)
71 | end
72 |
73 | def protocol
74 | get_header('SERVER_PROTOCOL')
75 | end
76 |
77 | def server_name
78 | get_header('SERVER_NAME')
79 | end
80 | end
81 | end
82 |
--------------------------------------------------------------------------------
/lib/active_endpoint/response.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | class Response
3 | def initialize(response)
4 | @response = response
5 | end
6 |
7 | def probe
8 | {
9 | body: body
10 | }
11 | end
12 |
13 | private
14 |
15 | def body
16 | @response.join if @response.respond_to? :join
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/active_endpoint/routes/blacklist.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | module Routes
3 | class Blacklist < Momento
4 | def initialize
5 | super(Array)
6 | end
7 |
8 | private
9 |
10 | def add_endpoint(options)
11 | @endpoints << fetch_endpoint(options)
12 | end
13 |
14 | def add_resources(options)
15 | resources = fetch_resources(options)
16 | actions = fetch_actions(options)
17 | scope = fetch_scope(options)
18 |
19 | if actions.present? && actions.any?
20 | temp_actions = []
21 | if resources.is_a?(Array)
22 | resources.each do |controller_name|
23 | actions.each { |action| temp_actions << "#{controller_name}##{action}" }
24 | end
25 | else
26 | actions.each { |action| temp_actions << "#{resources}##{action}" }
27 | end
28 | @actions += apply(scope, temp_actions)
29 | else
30 | temp_resources = []
31 | if resources.is_a?(Array)
32 | resources.each { |resource| temp_resources << resource }
33 | else
34 | temp_resources << resources
35 | end
36 | @resources += apply(scope, temp_resources)
37 | end
38 | end
39 |
40 | def add_scopes(options)
41 | scope = fetch_scope(options)
42 | @scopes << scope unless @scopes.include?(scope)
43 | end
44 |
45 | def present_endpoint?(request)
46 | @endpoints.include?(request[:endpoint])
47 | end
48 |
49 | def present_resource?(request)
50 | check_present(@resources, request)
51 | end
52 |
53 | def present_action?(request)
54 | @actions.include?(request[:endpoint])
55 | end
56 |
57 | def present_scope?(request)
58 | check_present(@scopes, request)
59 | end
60 |
61 | def apply(scope, collection)
62 | return collection unless scope.present?
63 | collection.map { |subject| "#{scope}/#{subject}" }
64 | end
65 | end
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/lib/active_endpoint/routes/cache/proxy.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | module Routes
3 | module Cache
4 | module Proxy
5 | class AdapterError < ::StandardError; end
6 |
7 | CLIENTS = {
8 | redis: 'RedisStoreProxy'
9 | }.freeze
10 |
11 | def self.build(adapter)
12 | unless CLIENTS.keys.include?(adapter)
13 | message "You try to use unsupported cache store adapter! #{adapter}\n"
14 | raise ActiveEndpoint::Routes::Cache::Proxy::AdapterError.new(message)
15 | end
16 |
17 | "ActiveEndpoint::Routes::Cache::Proxy::#{CLIENTS[adapter]}".constantize.new
18 | end
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/lib/active_endpoint/routes/cache/proxy/redis_store_proxy.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | module Routes
3 | module Cache
4 | module Proxy
5 | class RedisStoreProxy
6 | def initialize(cache_prefix = ActiveEndpoint.cache_prefix, store = ::Redis::Store.new)
7 | @prefix = cache_prefix
8 | @store = store
9 | end
10 |
11 | def read(unprefixed_key)
12 | @store.get("#{@prefix}:#{unprefixed_key}")
13 | end
14 |
15 | def write(unprefixed_key, value, expires_in = nil)
16 | store_key = store_key(unprefixed_key)
17 |
18 | if expires_in.present?
19 | @store.setex(store_key, expires_in, value)
20 | else
21 | @store.set(store_key, value)
22 | end
23 |
24 | true
25 | end
26 |
27 | def expires_in(unprefixed_key)
28 | time = @store.ttl(store_key(unprefixed_key)).to_i
29 | time == -1 || time == -2 ? 0 : time
30 | end
31 |
32 | private
33 |
34 | def store_key(unprefixed_key)
35 | "#{@prefix}:#{unprefixed_key}"
36 | end
37 | end
38 | end
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/lib/active_endpoint/routes/cache/store.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | module Routes
3 | module Cache
4 | class Store
5 | delegate :read, to: :@store
6 | delegate :write, to: :@store
7 | delegate :expires_in, to: :@store
8 |
9 | def initialize(cache_store_client = ActiveEndpoint.cache_store_client)
10 | @store = ActiveEndpoint::Routes::Cache::Proxy.build(cache_store_client)
11 | end
12 |
13 | def allow?(rule)
14 | rule_cache_allow?(rule) && storage_cache_allow?(rule)
15 | end
16 |
17 | def unregistred?(probe)
18 | path = probe[:path]
19 | read(path).present? ? false : write(path, :unregistred)
20 | end
21 |
22 | private
23 |
24 | def rule_cache_allow?(options)
25 | cache_allow?(:rule, options)
26 | end
27 |
28 | def storage_cache_allow?(options)
29 | cache_allow?(:storage, options) do |key, period|
30 | expired = {
31 | key: key,
32 | period: period
33 | }
34 |
35 | ActiveSupport::Notifications.instrument('active_endpoint.clean_expired', expired: expired)
36 | end
37 | end
38 |
39 | def cache_allow?(prefix, options, &block)
40 |
41 | key = "#{prefix}:#{options[:key]}"
42 | constraints = options[prefix]
43 |
44 | cache = read(key)
45 | limit = cache.nil? ? cache : cache.to_i
46 | period = expires_in(key)
47 |
48 | ActiveEndpoint.logger.debug('ActiveEndpoint::Cache::Store Prefix', prefix)
49 | ActiveEndpoint.logger.debug('ActiveEndpoint::Cache::Store Limit', limit)
50 | ActiveEndpoint.logger.debug('ActiveEndpoint::Cache::Store Period', period)
51 |
52 | limited = limit.present? && limit.zero?
53 | expired = period.zero?
54 |
55 | if limit.nil? && expired && block_given?
56 | yield(options[:key], constraints[:period])
57 | end
58 |
59 | if limit.present? && !expired
60 | return false if limited
61 | write(key, limit - 1, period)
62 | else
63 | write(key, constraints[:limit] - 1, constraints[:period])
64 | end
65 | end
66 | end
67 | end
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/lib/active_endpoint/routes/constraint_rule.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | module Routes
3 | class ConstraintRule
4 | include Optionable
5 | include Constraintable
6 |
7 | def initialize(request, constraints = ActiveEndpoint.constraints)
8 | @request = request
9 | @constraints = constraints
10 | end
11 |
12 | def rule
13 | {
14 | key: "#{prefix}:#{@request[:endpoint]}"
15 | }.merge(fetch_constraints)
16 | end
17 |
18 | private
19 |
20 | def prefix
21 | return :endpoints if @constraints.send(:present_endpoint?, @request)
22 | return :resources if @constraints.send(:present_resource?, @request)
23 | return :actions if @constraints.send(:present_action?, @request)
24 | return :scopes if @constraints.send(:present_scope?, @request)
25 | :defaults
26 | end
27 |
28 | def fetch_constraints
29 | if prefix == :defaults
30 | default_constraints
31 | else
32 | constraints = @constraints.public_send(prefix)[@request[:endpoint]]
33 | constraints.present? ? constraints : default_constraints
34 | end
35 | end
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/lib/active_endpoint/routes/constraints.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | module Routes
3 | class Constraints < Momento
4 | include Constraintable
5 |
6 | def initialize
7 | super(Hash)
8 | end
9 |
10 | def add(options)
11 | super(options) do |options|
12 | if fetch_rule(options).nil?
13 | message = "Constraints can't have empty limit and period!\n"
14 | raise ActiveEndpoint::Routes::Constraints::Error.new(message)
15 | end
16 | end
17 | end
18 |
19 | private
20 |
21 | def present_endpoint?(request)
22 | @endpoints.keys.include?(request[:endpoint])
23 | end
24 |
25 | def present_resource?(request)
26 | check_present(@resources.keys, request)
27 | end
28 |
29 | def present_action?(request)
30 | @actions.keys.include?(request[:endpoint])
31 | end
32 |
33 | def present_scope?(request)
34 | check_present(@scopes.keys, request)
35 | end
36 |
37 | def add_endpoint(options)
38 | @endpoints[fetch_endpoint(options)] = constraints(options)
39 | end
40 |
41 | def add_resources(options)
42 | resources = fetch_resources(options)
43 | actions = fetch_actions(options)
44 | scope = fetch_scope(options)
45 |
46 | if actions.present? && actions.any?
47 | temp_actions = {}
48 | if resources.is_a?(Array)
49 | resources.each do |controller_name|
50 | actions.each { |action| temp_actions["#{controller_name}##{action}"] = constraints(options) }
51 | end
52 | else
53 | actions.each { |action| temp_actions["#{resources}##{action}"] = constraints(options) }
54 | end
55 | @actions = @actions.merge(apply(scope, temp_actions))
56 | else
57 | temp_resources = {}
58 | if resources.is_a?(Array)
59 | resources.each { |resource| temp_resources[resource] = constraints(options) }
60 | else
61 | temp_resources[resources] = constraints(options)
62 | end
63 | @resources = @resources.merge(apply(scope, temp_resources))
64 | end
65 | end
66 |
67 | def add_scopes(options)
68 | @scopes[fetch_scope(options)] = constraints(options)
69 | end
70 |
71 | def apply(scope, collection)
72 | return collection unless scope.present?
73 |
74 | collection.inject({}) do |hash, (key, value)|
75 | hash["#{scope}/#{key}"] = value
76 | hash
77 | end
78 | end
79 | end
80 | end
81 | end
82 |
--------------------------------------------------------------------------------
/lib/active_endpoint/routes/matcher.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | module Routes
3 | class Matcher
4 | include RailsRoutable
5 |
6 | def initialize(blacklist = ActiveEndpoint.blacklist, cache_store = ActiveEndpoint::Routes::Cache::Store.new)
7 | @blacklist = blacklist
8 | @cache_store = cache_store
9 | end
10 |
11 | def whitelisted?(request)
12 | trackable?(request) && rails_action?(request)
13 | end
14 |
15 | def blacklisted?(probe)
16 | @blacklist.include?(probe)
17 | end
18 |
19 | def unregistred?(request)
20 | trackable?(request) && !rails_action(request)
21 | end
22 |
23 | def assets?(request)
24 | request.path.start_with?('/assets')
25 | end
26 |
27 | def allow_account?(request)
28 | @constraint_rule = ActiveEndpoint::Routes::ConstraintRule.new(request).rule
29 | @cache_store.allow?(@constraint_rule)
30 | end
31 |
32 | def allow_register?(request)
33 | @cache_store.unregistred?(request.probe)
34 | end
35 |
36 | private
37 |
38 | def favicon?(request)
39 | request.path == '/favicon.ico'
40 | end
41 |
42 | def engine?(request)
43 | request.path.include?('active_endpoint')
44 | end
45 |
46 | def trackable?(request)
47 | !(engine?(request) || assets?(request) || favicon?(request) || blacklisted?(request.probe))
48 | end
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/lib/active_endpoint/routes/momento.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | module Routes
3 | class Momento
4 | include Configurable
5 | include Optionable
6 |
7 | class Error < ::StandardError; end
8 |
9 | attr_reader :endpoints, :resources, :actions, :scopes
10 |
11 | def initialize(structure_class)
12 | @structure = structure_class
13 | @endpoints = structure_class.new
14 | @resources = structure_class.new
15 | @actions = structure_class.new
16 | @scopes = structure_class.new
17 | end
18 |
19 | def include?(request)
20 | [
21 | present_endpoint?(request),
22 | present_resource?(request),
23 | present_action?(request),
24 | present_scope?(request)
25 | ].any?
26 | end
27 |
28 | def add(**options, &block)
29 | yield(options) if block_given?
30 |
31 | add_endpoint(options) if fetch_endpoint(options).present?
32 | add_resources(options) if fetch_resources(options).present?
33 | add_scopes(options) if fetch_scope(options).present?
34 | end
35 |
36 | private
37 |
38 | def add_endpoint(_options)
39 | raise NotImplementedError
40 | end
41 |
42 | def add_resource(_options)
43 | raise NotImplementedError
44 | end
45 |
46 | def add_scopes(_options)
47 | raise NotImplementedError
48 | end
49 |
50 | def present_endpoint?(_request)
51 | raise NotImplementedError
52 | end
53 |
54 | def present_resource?(_request)
55 | raise NotImplementedError
56 | end
57 |
58 | def present_action?(_request)
59 | raise NotImplementedError
60 | end
61 |
62 | def present_scope?(_request)
63 | raise NotImplementedError
64 | end
65 |
66 | def apply(_scope, _collection)
67 | raise NotImplementedError
68 | end
69 |
70 | def check_present(collection, request)
71 | endpoint = request[:endpoint]
72 | collection.map { |subject| endpoint.present? && endpoint.start_with?(subject) }.any?
73 | end
74 | end
75 | end
76 | end
77 |
--------------------------------------------------------------------------------
/lib/active_endpoint/storage.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | class Storage
3 | STORING_FIELDS = %i[
4 | body
5 | response
6 | started_at
7 | finished_at
8 | duration
9 | endpoint
10 | ip
11 | params
12 | path
13 | request_method
14 | url
15 | xhr
16 | ].freeze
17 |
18 | LOGGING_FIELDS = %i[
19 | base_url
20 | content_charset
21 | content_length
22 | content_type
23 | fullpath
24 | http_version
25 | http_connection
26 | http_accept_encoding
27 | http_accept_language
28 | media_type
29 | media_type_params
30 | method
31 | path_info
32 | pattern
33 | port
34 | protocol
35 | server_name
36 | ssl
37 | ].freeze
38 |
39 | class << self
40 | private
41 |
42 | def notifier
43 | ActiveSupport::Notifications
44 | end
45 |
46 | def store!(params)
47 | handle_creation(ActiveEndpoint::Probe.new(params))
48 | end
49 |
50 | def register!(params)
51 | handle_creation(ActiveEndpoint::UnregistredProbe.new(params.merge(endpoint: :unregistred)))
52 | end
53 |
54 | def clean!(endpoint, period)
55 | ActiveEndpoint::Probe.registred.probe(endpoint).taken_before(period).destroy_all
56 | end
57 |
58 | def handle_creation(probe)
59 | probe.save
60 | rescue => error
61 | ActiveEndpoint.logger.error('ActiveEndpoint::Probe', error)
62 | end
63 |
64 | def probe_params(transaction_id, probe)
65 | request = probe.delete(:request)
66 | request_body = request.delete(:body)
67 |
68 | response = probe.delete(:response)
69 | response_body = response.present? ? response[:body] : nil
70 |
71 | params = {
72 | uuid: transaction_id,
73 | response: response_body ? Base64.encode64(response_body) : '',
74 | started_at: probe[:created_at],
75 | finished_at: probe[:finished_at],
76 | duration: probe[:finished_at] ? (probe[:finished_at] - probe[:created_at]).second.round(3) : 0,
77 | body: request_body.is_a?(Puma::NullIO) ? '' : request_body
78 | }.merge(request)
79 |
80 | [params.dup.except(*LOGGING_FIELDS), params.dup.except(*STORING_FIELDS)]
81 | end
82 | end
83 |
84 | notifier.subscribe('active_endpoint.tracked_probe') do |_name, _start, _ending, id, payload|
85 | store_params, logging_params = probe_params(id, payload[:probe])
86 |
87 | ActiveEndpoint.logger.info('ActiveEndpoint::Storage', logging_params)
88 |
89 | store!(store_params)
90 | end
91 |
92 | notifier.subscribe('active_endpoint.unregistred_probe') do |_name, _start, _ending, id, payload|
93 | store_params, logging_params = probe_params(id, payload[:probe])
94 |
95 | ActiveEndpoint.logger.debug('ActiveEndpoint::Storage', store_params)
96 | ActiveEndpoint.logger.debug('ActiveEndpoint::Storage', logging_params)
97 |
98 | register!(store_params)
99 | end
100 |
101 | notifier.subscribe('active_endpoint.clean_expired') do |_name, _start, _ending, id, payload|
102 | key = payload[:expired][:key].split(':').last
103 | period = DateTime.now - (payload[:expired][:period] * ActiveEndpoint.storage_keep_periods).seconds
104 |
105 | ActiveEndpoint.logger.info('ActiveEndpoint::Storage', { key: key, period: period, uuid: id })
106 |
107 | clean!(key, period)
108 | end
109 | end
110 | end
111 |
--------------------------------------------------------------------------------
/lib/active_endpoint/tags.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | class Tags
3 | include Configurable
4 |
5 | attr_reader :definition
6 |
7 | def initialize
8 | @definition = {}
9 | end
10 |
11 | def add(tag, condition)
12 | @definition[tag] = condition
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/active_endpoint/version.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | VERSION = '0.2.0'.freeze
3 | end
4 |
--------------------------------------------------------------------------------
/lib/generators/active_endpoint/install_generator.rb:
--------------------------------------------------------------------------------
1 | module ActiveEndpoint
2 | module Generators
3 | class InstallGenerator < ::Rails::Generators::Base
4 | include ::Rails::Generators::Migration
5 |
6 | source_root File.expand_path('../templates', __dir__)
7 |
8 | class << self
9 | def next_migration_number(_dirname)
10 | Time.current.strftime('%Y%m%d%H%M%S')
11 | end
12 | end
13 |
14 | puts '=> Creates a ActiveEndpoint initializer, migration and migrate data base for your application.'
15 |
16 | def copy_initializer_file
17 | puts '=> Copy initializer file to config/initializers/active_endpoint.rb'
18 | copy_file 'active_endpoint.rb', 'config/initializers/active_endpoint.rb'
19 | end
20 |
21 | def copy_migration_file
22 | puts '=> Create migration file in db/migrate/***_create_active_endpoint_probes.rb'
23 | migration_template 'migration.erb', 'db/migrate/create_active_endpoint_probes.rb',
24 | migration_version: migration_version
25 | end
26 |
27 | private
28 |
29 | def migration_version
30 | last_rails = ::Rails.version.start_with?('5')
31 | "[#{::Rails::VERSION::MAJOR}.#{::Rails::VERSION::MINOR}]" if last_rails
32 | end
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/lib/generators/templates/active_endpoint.rb:
--------------------------------------------------------------------------------
1 | ActiveEndpoint.configure do |endpoint|
2 | endpoint.blacklist.configure do |list|
3 | # list.add(endpoint: 'web/welcome#index')
4 | # list.add(resources: 'web/users', actions: ['show'])
5 | # list.add(scope: 'web', resources: 'users', actions: ['show'])
6 | # list.add(scope: 'web')
7 | end
8 |
9 | endpoint.constraint_limit = 100
10 | endpoint.constraint_period = 1.minute
11 |
12 | endpoint.storage_limit = 1000
13 | endpoint.storage_period = 1.day
14 | endpoint.storage_keep_periods = 2
15 |
16 | endpoint.constraints.configure do |constraints|
17 | # constraints.add(endpoint: 'web/welcome#index',
18 | # rule: { limit: 100, period: 1.minute },
19 | # storage: { limit: 1000, period: 1.week }
20 | # })
21 | # constraints.add(scope: 'web', resources: 'users', rule: { limit: 100 })
22 | # constraints.add(scope: 'web', resources: ['users'],
23 | # actions: [:show, :edit, :update],
24 | # rule: { limit: 10, period: 1.minute })
25 | # constraints.add(scope: 'web', rule: { limit: 100, period: 1.minute })
26 | end
27 |
28 | endpoint.tags.configure do |tags|
29 | # tags.add(:fast, { less_than: 250 })
30 | # tags.add(:normal, { greater_than_or_equal_to: 250, less_than: 500 })
31 | # tags.add(:slow, { greater_than_or_equal_to: 250, less_than: 500 })
32 | # tags.add(:acceptable, { greater_than_or_equal_to: 500, less_than: 100 })
33 | # tags.add(:need_optimization, { greater_than_or_equal_to: 1000 })
34 | end
35 |
36 | define_setting :logger, ActiveEndpoint::Logger
37 | end
38 |
--------------------------------------------------------------------------------
/lib/generators/templates/migration.erb:
--------------------------------------------------------------------------------
1 | class CreateActiveEndpointProbes < ActiveRecord::Migration<%= migration_version %>
2 | def change
3 | create_table :active_endpoint_probes do |t|
4 | t.string :type, default: ''
5 |
6 | t.string :uuid, null: false
7 | t.string :endpoint, null: false, index: true
8 | t.string :path, null: false
9 |
10 | t.string :query_string, null: false
11 | t.string :request_method, null: false
12 | t.string :ip, null: false
13 | t.string :url, null: false
14 |
15 | t.boolean :xhr
16 |
17 | t.datetime :started_at, null: false
18 | t.datetime :finished_at
19 |
20 | t.float :duration, nill: false
21 |
22 | t.json :params, default: '{}'
23 |
24 | t.text :response, default: ''
25 | t.text :body, default: ''
26 |
27 | t.timestamps
28 | end
29 | end
30 | end
--------------------------------------------------------------------------------
/spec/active_endpoint/concerns/configurabe_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe Configurable do
4 | module Test
5 | extend Configurable
6 | end
7 |
8 | describe '#configure' do
9 | before do
10 | Test.configure do |test|
11 | test.setting = :not_default
12 | end
13 | end
14 |
15 | subject { Test.setting }
16 |
17 | it { is_expected.to eql(:not_default) }
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/active_endpoint/concerns/constrainable_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe Constraintable do
4 | let(:constraint_limit) { 22 }
5 | let(:constraint_period) { 22.minutes }
6 | let(:storage_limit) { 2222 }
7 | let(:storage_period) { 2.week }
8 | let(:rule) { { limit: constraint_limit, period: constraint_period } }
9 | let(:storage) { { limit: storage_limit, period: storage_period } }
10 | let(:constraints) { { rule: rule, storage: storage } }
11 |
12 | before do
13 | module Test
14 | extend Optionable
15 | extend Constraintable
16 | end
17 |
18 | ActiveEndpoint.configure do |config|
19 | config.constraint_limit = constraint_limit
20 | config.constraint_period = constraint_period
21 | config.storage_limit = storage_limit
22 | config.storage_period = storage_period
23 | end
24 | end
25 |
26 | describe '#constraints' do
27 | context 'default' do
28 | context 'rule constraints' do
29 | it 'containts default rule' do
30 | expect(Test.default_rule_constraints).to eq(rule)
31 | end
32 |
33 | it 'containts default storage' do
34 | expect(Test.default_storage_constraints).to eq(storage)
35 | end
36 | end
37 | end
38 |
39 | context 'part of defaults' do
40 | context 'rule' do
41 | it 'constraints rule' do
42 | constraints = Test.rule_constraints(rule: { limit: 300 })
43 | result = { limit: 300, period: constraint_period }
44 | expect(constraints).to eq(result)
45 | end
46 | end
47 |
48 | context 'storage' do
49 | it 'constrainrts storage' do
50 | constraints = Test.storage_constraints(storage: { period: 2.minutes })
51 | result = { limit: storage_limit, period: 2.minutes }
52 | expect(constraints).to eq(result)
53 | end
54 | end
55 | end
56 |
57 | context 'storage constraints' do
58 | subject { Test.constraints(constraints) }
59 | it { is_expected.to eq(constraints) }
60 | end
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/spec/active_endpoint/concerns/optionable_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe Optionable do
4 | let(:empty_options) { {} }
5 |
6 | %i[
7 | endpoint
8 | actions
9 | resources
10 | scope
11 | limit
12 | period
13 | storage
14 | rule
15 | ].each do |method|
16 | describe "#fetch_#{method}" do
17 | let(:object) { Object.new }
18 | let(:options) { { method => true } }
19 |
20 | before do
21 | object.extend(described_class)
22 | end
23 |
24 | context 'if exists option' do
25 | subject { object.send("fetch_#{method}".to_sym, options) }
26 | it { is_expected.to be_truthy }
27 | end
28 |
29 | context 'if option empty' do
30 | subject { object.send("fetch_#{method}".to_sym, empty_options) }
31 | it { is_expected.to be_falsey }
32 | end
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/spec/active_endpoint/concerns/rails_routable_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe RailsRoutable do
4 | let(:request) { {} }
5 | let(:action) { { controller: 'web/welcome', action: 'index' } }
6 |
7 | before do
8 | module Test
9 | extend RailsRoutable
10 | end
11 |
12 | allow(Test).to receive(:rails_action).and_return(action)
13 | end
14 |
15 | describe '#rails_action?' do
16 | it { is_expected.to be_truthy }
17 | end
18 |
19 | describe '#rails_request_params' do
20 | subject { Test.rails_request_params(request) }
21 | it { is_expected.to eql({}) }
22 | end
23 |
24 | describe '#rails_endpoint' do
25 | subject { Test.rails_endpoint(request) }
26 | it { is_expected.to eql(action) }
27 | end
28 |
29 | describe '#rails_action' do
30 | end
31 |
32 | describe '#rails_route_pattern' do
33 | end
34 |
35 | describe '#rails_routes' do
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/spec/active_endpoint/concerns/settingable_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe Settingable do
4 | module Test
5 | extend Settingable
6 |
7 | define_setting :setting, :default
8 | end
9 |
10 | before do
11 | Test.configure do |test|
12 | test.setting = :not_default
13 | end
14 | end
15 |
16 | describe '#define_setting' do
17 | subject { Test.setting }
18 | it { is_expected.to eql(:not_default) }
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/spec/active_endpoint/extentions/active_record_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Extentions::ActiveRecord do
4 | describe '#tagged_as' do
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/spec/active_endpoint/logger_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Logger do
4 | describe '#info' do
5 | end
6 |
7 | describe '#debug' do
8 | end
9 |
10 | describe '#error' do
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/spec/active_endpoint/proxy_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Proxy do
4 | describe '#track' do
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/spec/active_endpoint/request_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Request do
4 | describe '#probe' do
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/spec/active_endpoint/response_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Response do
4 | describe '#body' do
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/spec/active_endpoint/routes/blacklist_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Routes::Blacklist do
4 | before do
5 | ActiveEndpoint.configure do |config|
6 | config.blacklist.configure do |list|
7 | list.add(endpoint: 'test#index')
8 | list.add(resources: 'test_resource')
9 | list.add(resources: 'test_actions', actions: ['show'])
10 | list.add(scope: 'web')
11 | end
12 | end
13 | end
14 |
15 | describe '#add' do
16 | context 'endpoint' do
17 | subject { ActiveEndpoint.blacklist.endpoints }
18 | it { is_expected.to include('test#index') }
19 | it { is_expected.not_to include('test_resources#show') }
20 | end
21 |
22 | context 'resource' do
23 | subject { ActiveEndpoint.blacklist.resources }
24 | it { is_expected.to include('test_resource') }
25 | it { is_expected.not_to include('test#index') }
26 | end
27 |
28 | context 'action' do
29 | subject { ActiveEndpoint.blacklist.actions }
30 | it { is_expected.to include('test_actions#show') }
31 | it { is_expected.not_to include('test_actions#index') }
32 | end
33 |
34 | context 'scope' do
35 | subject { ActiveEndpoint.blacklist.scopes }
36 | it { is_expected.to include('web') }
37 | it { is_expected.not_to include('test') }
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/spec/active_endpoint/routes/cache/proxy/redis_store_proxy_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Routes::Cache::Proxy::RedisStoreProxy do
4 | describe '#read' do
5 | end
6 |
7 | describe '#write' do
8 | end
9 |
10 | describe '#expires_in' do
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/spec/active_endpoint/routes/cache/proxy_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Routes::Cache::Proxy do
4 | describe '#build' do
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/spec/active_endpoint/routes/cache/store_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Routes::Cache::Store do
4 | describe '#allow?' do
5 | end
6 |
7 | describe '#unregistred?' do
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/active_endpoint/routes/constraint_rule_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Routes::ConstraintRule do
4 | let(:request) do
5 | {
6 | endpoint: 'test#index'
7 | }
8 | end
9 |
10 | let(:rule) do
11 | {
12 | key: 'defaults:test#index',
13 | rule: { limit: ActiveEndpoint.constraint_limit, period: ActiveEndpoint.constraint_period },
14 | storage: { limit: ActiveEndpoint.storage_limit, period: ActiveEndpoint.storage_period }
15 | }
16 | end
17 |
18 | describe '#rule' do
19 | describe ':key' do
20 | subject { ActiveEndpoint::Routes::ConstraintRule.new(request).rule[:key] }
21 | it { is_expected.to eql(rule[:key]) }
22 | end
23 |
24 | describe ':rule' do
25 | subject { ActiveEndpoint::Routes::ConstraintRule.new(request).rule[:rule] }
26 | it { is_expected.to eq(rule[:rule]) }
27 | end
28 |
29 | describe ':storage' do
30 | subject { ActiveEndpoint::Routes::ConstraintRule.new(request).rule[:storage] }
31 | it { is_expected.to eq(rule[:storage]) }
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/spec/active_endpoint/routes/constraints_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Routes::Constraints do
4 | let(:rule) { { limit: 1, period: 2.seconds } }
5 |
6 | before do
7 | ActiveEndpoint.configure do |config|
8 | config.constraints.configure do |list|
9 | list.add(endpoint: 'test#index', rule: rule)
10 | list.add(resources: 'test_resource', rule: rule)
11 | list.add(resources: 'test_actions', actions: ['show'], rule: rule)
12 | list.add(scope: 'web', rule: rule)
13 | end
14 | end
15 | end
16 |
17 | describe '#add' do
18 | context 'endpoint' do
19 | subject { ActiveEndpoint.constraints.endpoints.keys }
20 | it { is_expected.to include('test#index') }
21 | it { is_expected.not_to include('test_resources#show') }
22 | end
23 |
24 | context 'resource' do
25 | subject { ActiveEndpoint.constraints.resources.keys }
26 | it { is_expected.to include('test_resource') }
27 | it { is_expected.not_to include('test#index') }
28 | end
29 |
30 | context 'action' do
31 | subject { ActiveEndpoint.constraints.actions.keys }
32 | it { is_expected.to include('test_actions#show') }
33 | it { is_expected.not_to include('test_actions#index') }
34 | end
35 |
36 | context 'scope' do
37 | subject { ActiveEndpoint.constraints.scopes.keys }
38 | it { is_expected.to include('web') }
39 | it { is_expected.not_to include('test') }
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/spec/active_endpoint/routes/matcher_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Routes::Matcher do
4 | let(:request) { double('Request', probe: { endpoint: 'welcome#index' }) }
5 | let(:cache_store) { double('Cache::Store') }
6 | let(:constraint_rule) { double('ConstraintRule', rule: {}) }
7 | let(:matcher) { described_class.new }
8 |
9 | before do
10 | allow(ActiveEndpoint::Routes::ConstraintRule).to receive(:new).and_return(constraint_rule)
11 | allow(ActiveEndpoint::Routes::Cache::Store).to receive(:new).and_return(cache_store)
12 | allow(cache_store).to receive(:allow?).and_return(true)
13 | allow(cache_store).to receive(:unregistred?).and_return(true)
14 | allow(request).to receive(:path).and_return('/')
15 | end
16 |
17 | describe '#whitelisted?' do
18 | subject { matcher.whitelisted?(request) }
19 | xit { is_expected.to be_falsey }
20 | end
21 |
22 | describe '#blacklisted' do
23 | let(:probe) { { endpoint: 'welcome#index' } }
24 | context 'ignored' do
25 | subject { matcher.blacklisted?(probe) }
26 | it { is_expected.to be_falsey }
27 | end
28 |
29 | context 'not ignored' do
30 | subject { matcher.blacklisted?(probe) }
31 | it { is_expected.to be_falsey }
32 | end
33 | end
34 |
35 | describe '#unregistred?' do
36 | subject { matcher.unregistred?(request) }
37 | xit { is_expected.to be_falsey }
38 | end
39 |
40 | describe '#assets?' do
41 | let(:probe) { double('Probe', path: '/assets') }
42 | subject { matcher.assets?(probe) }
43 | it { is_expected.to be_truthy }
44 | end
45 |
46 | describe '#allow_account?' do
47 | subject { matcher.allow_account?(request) }
48 | it { is_expected.to be_truthy }
49 | end
50 |
51 | describe '#allow_register?' do
52 | subject { matcher.allow_register?(request) }
53 | it { is_expected.to be_truthy }
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/spec/active_endpoint/routes/momento_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Routes::Momento do
4 | describe '#initialize' do
5 | subject { described_class.new(Array).scopes }
6 | it { is_expected.to eql([]) }
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/spec/active_endpoint/storage_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Storage do
4 | describe 'creates probe' do
5 | end
6 |
7 | describe 'creates unregistred probe' do
8 | end
9 |
10 | describe 'removes expired probes' do
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/spec/active_endpoint/tags_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe ActiveEndpoint::Tags do
4 | describe '#add' do
5 | let(:tag_name) { :tag_name }
6 | let(:definition) { :definition }
7 | let(:tags) { ActiveEndpoint::Tags.new }
8 |
9 | before do
10 | tags.configure do |tag|
11 | tag.add(tag_name, definition)
12 | end
13 | end
14 |
15 | subject { tags.definition[tag_name] }
16 |
17 | it { is_expected.to eql(definition) }
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'bundler/setup'
2 | require 'active_endpoint'
3 |
4 | RSpec.configure do |config|
5 | config.example_status_persistence_file_path = '.rspec_status'
6 |
7 | config.expect_with :rspec do |c|
8 | c.syntax = :expect
9 | end
10 | end
11 |
--------------------------------------------------------------------------------