putsreq 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.babelrc +26 -0
- data/.codeclimate.yml +29 -5
- data/.env.docker +6 -0
- data/.eslintignore +4 -0
- data/.eslintrc +22 -0
- data/.gitignore +8 -0
- data/.postcssrc.yml +3 -0
- data/.prettierrc +4 -0
- data/.reek +12 -0
- data/.rubocop.yml +47 -25
- data/.ruby-version +1 -1
- data/.simplecov +6 -0
- data/.travis.yml +38 -4
- data/Dockerfile +44 -0
- data/Gemfile +40 -61
- data/Gemfile.lock +294 -186
- data/Procfile +1 -1
- data/README.md +51 -46
- data/Rakefile +1 -1
- data/{public → app/assets}/images/logo.png +0 -0
- data/app/assets/javascripts/application.js +1 -1
- data/app/assets/javascripts/buckets.js.coffee +1 -21
- data/app/assets/stylesheets/{application.css → application.scss} +2 -1
- data/app/assets/stylesheets/buckets.css.scss +0 -2
- data/app/controllers/application_controller.rb +11 -9
- data/app/controllers/buckets_controller.rb +24 -18
- data/app/controllers/home_controller.rb +5 -5
- data/app/controllers/requests_controller.rb +13 -2
- data/app/helpers/application_helper.rb +6 -2
- data/app/interactors/create_or_retrieve_bucket.rb +17 -0
- data/app/interactors/create_request.rb +25 -7
- data/app/interactors/eval_response_builder.rb +9 -9
- data/app/interactors/filter_headers.rb +3 -3
- data/app/interactors/forward_request.rb +4 -4
- data/app/interactors/record_request.rb +1 -1
- data/app/interactors/track.rb +44 -0
- data/app/javascript/actionTypes.js +7 -0
- data/app/javascript/actions/index.js +64 -0
- data/app/javascript/components/Bucket.jsx +91 -0
- data/app/javascript/components/Pagination.jsx +67 -0
- data/app/javascript/components/RequestCount.jsx +21 -0
- data/app/javascript/components/request/Header.jsx +31 -0
- data/app/javascript/components/request/Response.jsx +28 -0
- data/app/javascript/components/request/index.jsx +47 -0
- data/app/javascript/packs/application.js +25 -0
- data/app/javascript/packs/hello_react.jsx +26 -0
- data/app/javascript/reducers/index.js +23 -0
- data/app/javascript/request_poller.js +19 -0
- data/app/javascript/store.js +9 -0
- data/app/models/bucket.rb +16 -10
- data/app/models/request.rb +2 -3
- data/app/models/user.rb +2 -2
- data/app/serializers/bucket_serializer.rb +11 -0
- data/app/serializers/request_serializer.rb +69 -0
- data/app/views/buckets/_buttons.html.erb +3 -3
- data/app/views/buckets/_form.html.erb +6 -6
- data/app/views/buckets/_readonly_buttons.html.erb +1 -1
- data/app/views/buckets/show.html.erb +12 -25
- data/app/views/home/index.html.erb +22 -16
- data/app/views/layouts/application.html.erb +21 -22
- data/app/views/layouts/devise.html.erb +16 -18
- data/app/views/shared/_flash.html.erb +3 -1
- data/bin/byebug +21 -0
- data/bin/cc-tddium-post-worker +21 -0
- data/bin/codeclimate-test-reporter +21 -0
- data/bin/coderay +21 -0
- data/bin/dotenv +21 -0
- data/bin/htmldiff +21 -0
- data/bin/httparty +21 -0
- data/bin/httpclient +21 -0
- data/bin/ldiff +21 -0
- data/bin/mongo_console +21 -0
- data/bin/nokogiri +21 -0
- data/bin/pry +21 -0
- data/bin/putsreq +18 -71
- data/bin/rackup +21 -0
- data/bin/rails +1 -1
- data/bin/ri +21 -0
- data/bin/rollbar-rails-runner +21 -0
- data/bin/rspec +10 -5
- data/bin/safe_yaml +21 -0
- data/bin/sass +21 -0
- data/bin/sass-convert +21 -0
- data/bin/scss +21 -0
- data/bin/sdoc +21 -0
- data/bin/sdoc-merge +21 -0
- data/bin/setup +38 -0
- data/bin/sprockets +21 -0
- data/bin/thor +21 -0
- data/bin/tilt +21 -0
- data/bin/unicorn +21 -0
- data/bin/unicorn_rails +21 -0
- data/bin/update +29 -0
- data/bin/webpack +15 -0
- data/bin/webpack-dev-server +15 -0
- data/bin/yarn +11 -0
- data/config/application.rb +6 -4
- data/config/boot.rb +1 -1
- data/config/environment.rb +1 -1
- data/config/environments/development.rb +7 -3
- data/config/environments/production.rb +5 -2
- data/config/environments/test.rb +3 -1
- data/config/initializers/devise.rb +2 -2
- data/config/initializers/rack_attack.rb +24 -0
- data/config/initializers/rollbar.rb +58 -0
- data/config/initializers/setup_email.rb +7 -7
- data/config/mongoid.yml +2 -2
- data/config/newrelic.yml +45 -0
- data/config/puma.rb +15 -0
- data/config/routes.rb +2 -0
- data/config/webpack/development.js +5 -0
- data/config/webpack/environment.js +3 -0
- data/config/webpack/production.js +5 -0
- data/config/webpack/test.js +5 -0
- data/config/webpacker.yml +70 -0
- data/docker-compose.yml +25 -0
- data/lib/putsreq/cli_helper.rb +114 -0
- data/lib/putsreq/version.rb +1 -1
- data/package.json +30 -0
- data/putsreq.gemspec +6 -7
- data/spec/controllers/buckets_controller_spec.rb +54 -46
- data/spec/interactors/create_or_retrieve_bucket_spec.rb +25 -0
- data/spec/interactors/create_request_spec.rb +11 -8
- data/spec/interactors/create_response_spec.rb +1 -1
- data/spec/interactors/eval_response_builder_spec.rb +14 -17
- data/spec/interactors/forward_request_spec.rb +4 -4
- data/spec/lib/putsreq/cli_helper_spec.rb +114 -0
- data/spec/models/bucket_spec.rb +3 -3
- data/spec/spec_helper.rb +25 -25
- data/yarn.lock +7043 -0
- metadata +85 -20
- data/.hound.yml +0 -14
- data/app/interactors/notify_count.rb +0 -15
- data/app/models/.keep +0 -0
- data/config/initializers/redis.rb +0 -17
- data/config/unicorn.rb +0 -16
- data/lib/assets/.keep +0 -0
- data/public/images/.keep +0 -0
- data/vendor/assets/javascripts/.keep +0 -0
- data/vendor/assets/stylesheets/.keep +0 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateOrRetrieveBucket
|
2
|
+
include Interactor
|
3
|
+
|
4
|
+
delegate :token, :owner_token, :user_id, to: :context
|
5
|
+
|
6
|
+
def call
|
7
|
+
if (bucket = Bucket.where(token: token).first)
|
8
|
+
return context.bucket = bucket
|
9
|
+
end
|
10
|
+
|
11
|
+
context.bucket = Bucket.create(
|
12
|
+
owner_token: owner_token,
|
13
|
+
user_id: user_id,
|
14
|
+
token: token
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
@@ -4,12 +4,30 @@ class CreateRequest
|
|
4
4
|
delegate :bucket, :rack_request, to: :context
|
5
5
|
|
6
6
|
def call
|
7
|
-
context.request = bucket.requests.create(
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
context.request = bucket.requests.create(
|
8
|
+
body: body(rack_request),
|
9
|
+
content_length: rack_request.content_length,
|
10
|
+
request_method: rack_request.request_method,
|
11
|
+
ip: rack_request.ip,
|
12
|
+
url: rack_request.url,
|
13
|
+
headers: FilterHeaders.call(headers: rack_request.env).headers
|
14
|
+
)
|
15
|
+
|
16
|
+
context.params = params
|
17
|
+
rescue
|
18
|
+
Rollbar.scope!(request: context.request)
|
19
|
+
raise
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def params
|
25
|
+
rack_request.params.to_h.except('controller', 'action', 'token')
|
26
|
+
end
|
27
|
+
|
28
|
+
def body(rack_request)
|
29
|
+
body = rack_request.body.read.encode('UTF-8', invalid: :replace, undef: :replace)
|
30
|
+
|
31
|
+
body.is_utf8? ? body : nil
|
14
32
|
end
|
15
33
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class EvalResponseBuilder
|
2
2
|
include Interactor
|
3
3
|
|
4
|
-
delegate :request, to: :context
|
4
|
+
delegate :request, :params, to: :context
|
5
5
|
|
6
6
|
def call
|
7
7
|
v8_ctx = V8::Context.new timeout: timeout
|
@@ -12,9 +12,9 @@ class EvalResponseBuilder
|
|
12
12
|
|
13
13
|
eval_response(v8_ctx)
|
14
14
|
rescue => e
|
15
|
-
context.built_response = { 'status'
|
15
|
+
context.built_response = { 'status' => 500,
|
16
16
|
'headers' => { 'Content-Type' => 'text/plain' },
|
17
|
-
'body'
|
17
|
+
'body' => e.message }
|
18
18
|
end
|
19
19
|
|
20
20
|
private
|
@@ -28,19 +28,19 @@ class EvalResponseBuilder
|
|
28
28
|
|
29
29
|
# filter allowed parameters
|
30
30
|
context.built_response = JSON.parse(context.built_response).select do |key, value|
|
31
|
-
%w
|
31
|
+
%w[status headers body].include?(key) && value.to_s.present?
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
def initialize_response_builder_attrs(v8_ctx)
|
36
|
-
v8_ctx['response'] = { 'status'
|
36
|
+
v8_ctx['response'] = { 'status' => 200,
|
37
37
|
'headers' => {},
|
38
|
-
'body'
|
38
|
+
'body' => 'ok' }
|
39
39
|
|
40
40
|
v8_ctx['request'] = { 'request_method' => request.request_method,
|
41
|
-
'body'
|
42
|
-
'params'
|
43
|
-
'headers'
|
41
|
+
'body' => request.body,
|
42
|
+
'params' => params,
|
43
|
+
'headers' => request.headers }
|
44
44
|
end
|
45
45
|
|
46
46
|
def eval_response_builder(v8_ctx)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
class FilterHeaders
|
2
2
|
include Interactor
|
3
3
|
|
4
|
-
BLACKLIST_HEADERS = %w
|
5
|
-
WHITELIST_HEADERS = %w
|
4
|
+
BLACKLIST_HEADERS = %w[HOST CF X-REQUEST X-FORWARDED CONNECT-TIME TOTAL-ROUTE-TIME VIA].freeze
|
5
|
+
WHITELIST_HEADERS = %w[HTTP_ CONTENT].freeze
|
6
6
|
|
7
7
|
delegate :headers, to: :context
|
8
8
|
|
@@ -14,7 +14,7 @@ class FilterHeaders
|
|
14
14
|
|
15
15
|
def client_supplied_headers
|
16
16
|
headers.to_h.each_with_object({}) do |(key, value), h|
|
17
|
-
next
|
17
|
+
next if value.to_s.blank?
|
18
18
|
|
19
19
|
next unless key.upcase.start_with?(*WHITELIST_HEADERS)
|
20
20
|
|
@@ -9,9 +9,9 @@ class ForwardRequest
|
|
9
9
|
# use the response from the forwarded URL
|
10
10
|
context.built_response = forward_to(built_request, forward_url)
|
11
11
|
rescue => e
|
12
|
-
context.built_response = { 'status'
|
12
|
+
context.built_response = { 'status' => 500,
|
13
13
|
'headers' => { 'Content-Type' => 'text/plain' },
|
14
|
-
'body'
|
14
|
+
'body' => e.message }
|
15
15
|
end
|
16
16
|
|
17
17
|
private
|
@@ -23,9 +23,9 @@ class ForwardRequest
|
|
23
23
|
|
24
24
|
forwarded_response = HTTParty.send(built_request['request_method'].downcase.to_sym, forward_url, options)
|
25
25
|
|
26
|
-
{ 'status'
|
26
|
+
{ 'status' => forwarded_response.code,
|
27
27
|
'headers' => FilterHeaders.call(headers: forwarded_response.headers).headers,
|
28
|
-
'body'
|
28
|
+
'body' => forwarded_response.body }
|
29
29
|
end
|
30
30
|
|
31
31
|
def body
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Track
|
2
|
+
include Interactor
|
3
|
+
|
4
|
+
delegate :bucket, :rack_request, to: :context
|
5
|
+
delegate :token, to: :bucket
|
6
|
+
delegate :ip, :user_agent, to: :rack_request
|
7
|
+
|
8
|
+
def call
|
9
|
+
return unless enabled?
|
10
|
+
|
11
|
+
tracker.event(category: 'bucket', action: 'record', label: token, value: 1) # , non_interactive: true)
|
12
|
+
rescue => e
|
13
|
+
Rollbar.error(e, token: token)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def global_context
|
19
|
+
{
|
20
|
+
user_ip: ip,
|
21
|
+
ssl: true,
|
22
|
+
user_agent: user_agent
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def client_id
|
27
|
+
# since there's no identification in the requests,
|
28
|
+
# the only way to aggregate requests per "user" is per ip
|
29
|
+
# https://stackoverflow.com/a/31854739/464685
|
30
|
+
ip
|
31
|
+
end
|
32
|
+
|
33
|
+
def enabled?
|
34
|
+
google_analytics_tracking_id.present?
|
35
|
+
end
|
36
|
+
|
37
|
+
def google_analytics_tracking_id
|
38
|
+
ENV['GOOGLE_ANALYTICS_TRACKING_ID']
|
39
|
+
end
|
40
|
+
|
41
|
+
def tracker
|
42
|
+
@_tracker ||= Staccato.tracker(google_analytics_tracking_id, client_id, global_context)
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
import fetch from 'cross-fetch'
|
2
|
+
import { bucketsActions } from '../actionTypes'
|
3
|
+
|
4
|
+
const getJSONFromPage = id => JSON.parse(document.getElementById(id).innerText)
|
5
|
+
|
6
|
+
const fetchFromPage = () => {
|
7
|
+
const bucket = getJSONFromPage('bucket-json')
|
8
|
+
favicon.badge(bucket.requests_count)
|
9
|
+
|
10
|
+
return {
|
11
|
+
type: bucketsActions.populate,
|
12
|
+
bucket,
|
13
|
+
page: 1
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
const favicon = new Favico({ bgColor: '#6C92C8', animation: 'none' })
|
18
|
+
|
19
|
+
const updateRequestsCount = count => {
|
20
|
+
return (dispatch, getState) => {
|
21
|
+
if (count === getState().bucket.requests_count) {
|
22
|
+
return
|
23
|
+
}
|
24
|
+
|
25
|
+
favicon.badge(count)
|
26
|
+
|
27
|
+
let page = getState().bucket.page || 1
|
28
|
+
|
29
|
+
if (count > getState().bucket.request_count) {
|
30
|
+
page = count - getState().bucket.requests_count + page
|
31
|
+
}
|
32
|
+
|
33
|
+
dispatch({
|
34
|
+
type: bucketsActions.updateRequestsCount,
|
35
|
+
requests_count: count,
|
36
|
+
page: page
|
37
|
+
})
|
38
|
+
|
39
|
+
if (count === 1) {
|
40
|
+
return fetchPage(1)(dispatch, getState)
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
const handlePageChange = page => {
|
46
|
+
return (dispatch, getState) => {
|
47
|
+
dispatch({ type: bucketsActions.loading })
|
48
|
+
|
49
|
+
fetchPage(page)(dispatch, getState)
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
const fetchPage = page => {
|
54
|
+
return (dispatch, getState) => {
|
55
|
+
fetch(`${getState().bucket.path}.json?page=${page}`)
|
56
|
+
.then(
|
57
|
+
response => response.json(),
|
58
|
+
error => console.log('An error occurred.', error)
|
59
|
+
)
|
60
|
+
.then(bucket => dispatch({ type: bucketsActions.populate, bucket, page }))
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
export { fetchFromPage, handlePageChange, updateRequestsCount }
|
@@ -0,0 +1,91 @@
|
|
1
|
+
import React, { Component } from 'react'
|
2
|
+
import { connect } from 'react-redux'
|
3
|
+
import Request from './request'
|
4
|
+
import Pagination from './Pagination'
|
5
|
+
import { fetchFromPage, handlePageChange } from '../actions'
|
6
|
+
import PropTypes from 'prop-types'
|
7
|
+
|
8
|
+
class Bucket extends Component {
|
9
|
+
componentWillMount() {
|
10
|
+
this.props.fetchFromPage()
|
11
|
+
}
|
12
|
+
|
13
|
+
renderFirstRequestLink() {
|
14
|
+
if(!this.props.bucket.request) { return }
|
15
|
+
|
16
|
+
return (
|
17
|
+
<em>Request ID: <a href={this.props.bucket.request.path} target="_blank">{this.props.bucket.request.id}</a></em>
|
18
|
+
)
|
19
|
+
}
|
20
|
+
|
21
|
+
renderFirstRequest() {
|
22
|
+
if(!this.props.bucket.first_request) { return }
|
23
|
+
|
24
|
+
return (
|
25
|
+
<p>
|
26
|
+
<em>First request at: {this.props.bucket.first_request.created_at}</em> <br />
|
27
|
+
<em>Last request at: {this.props.bucket.last_request.created_at}</em> <br />
|
28
|
+
|
29
|
+
{this.renderFirstRequestLink()}
|
30
|
+
</p>
|
31
|
+
)
|
32
|
+
}
|
33
|
+
|
34
|
+
handlePageChange(page) {
|
35
|
+
this.props.handlePageChange(page)
|
36
|
+
}
|
37
|
+
|
38
|
+
renderPagination() {
|
39
|
+
if(!this.props.bucket.request) { return }
|
40
|
+
|
41
|
+
return (
|
42
|
+
<Pagination
|
43
|
+
pageCount={this.props.bucket.requests_count}
|
44
|
+
page={this.props.bucket.page}
|
45
|
+
onPageChange={this.handlePageChange.bind(this)}
|
46
|
+
/>
|
47
|
+
)
|
48
|
+
}
|
49
|
+
|
50
|
+
renderRequestOrLoading() {
|
51
|
+
if(this.props.bucket.loading) {
|
52
|
+
return (
|
53
|
+
<div><em><strong>Loading...</strong></em></div>
|
54
|
+
)
|
55
|
+
}
|
56
|
+
|
57
|
+
return (<Request {...this.props.bucket.request} />)
|
58
|
+
}
|
59
|
+
|
60
|
+
render() {
|
61
|
+
if(!this.props.bucket) { return }
|
62
|
+
|
63
|
+
return (
|
64
|
+
<div>
|
65
|
+
<div className="row">
|
66
|
+
<div className="col-md-6">
|
67
|
+
{this.renderFirstRequest()}
|
68
|
+
</div>
|
69
|
+
<div className="col-md-6"></div>
|
70
|
+
</div>
|
71
|
+
{this.renderPagination()}
|
72
|
+
{this.renderRequestOrLoading()}
|
73
|
+
</div>
|
74
|
+
)
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
Bucket.propTypes = {
|
79
|
+
bucket: PropTypes.object.isRequired
|
80
|
+
}
|
81
|
+
|
82
|
+
const mapStateToProps = (state) => ({
|
83
|
+
bucket: state.bucket
|
84
|
+
})
|
85
|
+
|
86
|
+
const mapDispatchToProps = {
|
87
|
+
handlePageChange: handlePageChange,
|
88
|
+
fetchFromPage: fetchFromPage
|
89
|
+
}
|
90
|
+
|
91
|
+
export default connect(mapStateToProps, mapDispatchToProps)(Bucket)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import React, { Component } from 'react'
|
2
|
+
import PropTypes from 'prop-types'
|
3
|
+
|
4
|
+
export default class Pagination extends Component {
|
5
|
+
handleFirstPage() {
|
6
|
+
this.props.onPageChange(1)
|
7
|
+
}
|
8
|
+
|
9
|
+
handlePreviousPage() {
|
10
|
+
const previousPage = this.props.page - 1
|
11
|
+
if(previousPage > 0) { this.props.onPageChange(previousPage) }
|
12
|
+
}
|
13
|
+
|
14
|
+
handleNextPage() {
|
15
|
+
const nextPage = this.props.page + 1
|
16
|
+
if(nextPage <= this.props.pageCount) { this.props.onPageChange(nextPage) }
|
17
|
+
}
|
18
|
+
|
19
|
+
handleLastPage() {
|
20
|
+
this.props.onPageChange(this.props.pageCount)
|
21
|
+
}
|
22
|
+
|
23
|
+
firstClassNames() {
|
24
|
+
if(this.props.page <= 1) { return 'disabled' }
|
25
|
+
}
|
26
|
+
|
27
|
+
previousClassNames() {
|
28
|
+
const previousPage = this.props.page - 1
|
29
|
+
|
30
|
+
if(previousPage <= 0) { return 'disabled' }
|
31
|
+
}
|
32
|
+
|
33
|
+
nextClassNames() {
|
34
|
+
const nextPage = this.props.page + 1
|
35
|
+
|
36
|
+
if(nextPage > this.props.pageCount) { return 'disabled' }
|
37
|
+
}
|
38
|
+
|
39
|
+
lastClassNames() {
|
40
|
+
if(this.props.page >= this.props.pageCount) { return 'disabled' }
|
41
|
+
}
|
42
|
+
|
43
|
+
render() {
|
44
|
+
return(
|
45
|
+
<ul className="pagination">
|
46
|
+
<li className={this.firstClassNames()}>
|
47
|
+
<a onClick={this.handleFirstPage.bind(this)}>First</a>
|
48
|
+
</li>
|
49
|
+
<li className={this.previousClassNames()}>
|
50
|
+
<a onClick={this.handlePreviousPage.bind(this)}>Previous</a>
|
51
|
+
</li>
|
52
|
+
<li className={this.nextClassNames()}>
|
53
|
+
<a onClick={this.handleNextPage.bind(this)}>Next</a>
|
54
|
+
</li>
|
55
|
+
<li className={this.lastClassNames()}>
|
56
|
+
<a onClick={this.handleLastPage.bind(this)}>Last</a>
|
57
|
+
</li>
|
58
|
+
</ul>
|
59
|
+
)
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
Pagination.propTypes = {
|
64
|
+
page: PropTypes.number.isRequired,
|
65
|
+
pageCount: PropTypes.number.isRequired,
|
66
|
+
onPageChange: PropTypes.func.isRequired
|
67
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import React, { Component } from 'react'
|
2
|
+
import { connect } from 'react-redux'
|
3
|
+
import PropTypes from 'prop-types'
|
4
|
+
|
5
|
+
class RequestCount extends Component {
|
6
|
+
render() {
|
7
|
+
return (
|
8
|
+
<h3>{this.props.requests_count || 0}</h3>
|
9
|
+
)
|
10
|
+
}
|
11
|
+
}
|
12
|
+
|
13
|
+
RequestCount.propTypes = {
|
14
|
+
requests_count: PropTypes.number
|
15
|
+
}
|
16
|
+
|
17
|
+
const mapStateToProps = (state) => ({
|
18
|
+
requests_count: state.bucket.requests_count
|
19
|
+
})
|
20
|
+
|
21
|
+
export default connect(mapStateToProps)(RequestCount)
|