web_fetch 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WebFetch
4
- VERSION = '0.4.0'
4
+ VERSION = '0.5.0'
5
5
  end
@@ -0,0 +1,64 @@
1
+ .gitignore
2
+ .rspec
3
+ .rubocop.yml
4
+ .ruby-version
5
+ .strong_versions.yml
6
+ Gemfile
7
+ LICENSE
8
+ Makefile
9
+ README.md
10
+ Rakefile
11
+ TODO
12
+ bin/rspec
13
+ bin/rubocop
14
+ bin/strong_versions
15
+ bin/web_fetch_control
16
+ bin/web_fetch_server
17
+ config/locales/en.yml
18
+ doc/examples/blocking_requests.rb
19
+ doc/examples/non_blocking_requests.rb
20
+ doc/examples/use_uid_for_request.rb
21
+ doc/web_fetch_architecture.png
22
+ docker/Dockerfile
23
+ lib/web_fetch.rb
24
+ lib/web_fetch/client.rb
25
+ lib/web_fetch/concerns/client_http.rb
26
+ lib/web_fetch/concerns/http_helpers.rb
27
+ lib/web_fetch/concerns/validatable.rb
28
+ lib/web_fetch/errors.rb
29
+ lib/web_fetch/gatherer.rb
30
+ lib/web_fetch/helpers.rb
31
+ lib/web_fetch/logger.rb
32
+ lib/web_fetch/promise.rb
33
+ lib/web_fetch/request.rb
34
+ lib/web_fetch/resources.rb
35
+ lib/web_fetch/response.rb
36
+ lib/web_fetch/retriever.rb
37
+ lib/web_fetch/router.rb
38
+ lib/web_fetch/server.rb
39
+ lib/web_fetch/storage.rb
40
+ lib/web_fetch/storage/memcached.rb
41
+ lib/web_fetch/storage/memory.rb
42
+ lib/web_fetch/storage/redis.rb
43
+ lib/web_fetch/version.rb
44
+ manifest
45
+ spec/client_spec.rb
46
+ spec/concerns/validatable_spec.rb
47
+ spec/features/http_fetching_spec.rb
48
+ spec/gatherer_spec.rb
49
+ spec/helpers_spec.rb
50
+ spec/i18n_spec.rb
51
+ spec/promise_spec.rb
52
+ spec/request_spec.rb
53
+ spec/resources_spec.rb
54
+ spec/response_spec.rb
55
+ spec/retriever_spec.rb
56
+ spec/router_spec.rb
57
+ spec/server_spec.rb
58
+ spec/spec_helper.rb
59
+ spec/storage/memcached_spec.rb
60
+ spec/storage/memory_spec.rb
61
+ spec/storage/redis_spec.rb
62
+ spec/storage/shared_examples.rb
63
+ swagger.yaml
64
+ web_fetch.gemspec
@@ -65,13 +65,10 @@ describe WebFetch::Client do
65
65
 
66
66
  it { is_expected.to be_a WebFetch::Response }
67
67
 
68
- context 'no matching request found' do
69
- subject { proc { client.fetch('not-found') } }
70
- it { is_expected.to raise_error WebFetch::RequestNotFoundError }
68
+ context 'pending' do
69
+ subject { client.fetch('not-ready-yet') }
70
+ it { is_expected.to be_pending }
71
71
  end
72
-
73
- # Tested more extensively in supporting methods #retrieve_by_uid and
74
- # #find_by_uid below
75
72
  end
76
73
 
77
74
  describe '#retrieve_by_uid' do
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  describe WebFetch::Gatherer do
4
- let(:server) { WebFetch::MockServer.new }
4
+ let(:storage) { double }
5
5
 
6
6
  let(:valid_params) do
7
7
  { requests: [
@@ -11,31 +11,33 @@ describe WebFetch::Gatherer do
11
11
  end
12
12
 
13
13
  it 'is initialisable with params' do
14
- expect(described_class.new(server, valid_params)).to be_a described_class
14
+ expect(described_class.new(storage, valid_params)).to be_a described_class
15
15
  end
16
16
 
17
17
  describe 'validation' do
18
18
  context 'invalid' do
19
19
  it 'is invalid if `requests` parameter is not passed' do
20
- gatherer = described_class.new(server, {})
20
+ gatherer = described_class.new(storage, {})
21
21
  expect(gatherer.valid?).to be false
22
22
  expect(gatherer.errors).to include I18n.t(:requests_missing)
23
23
  end
24
24
 
25
25
  it 'is invalid if `requests` is not an array parameter' do
26
- gatherer = described_class.new(server, requests: 'hello')
26
+ gatherer = described_class.new(storage, requests: 'hello')
27
27
  expect(gatherer.valid?).to be false
28
28
  expect(gatherer.errors).to include I18n.t(:requests_not_array)
29
29
  end
30
30
 
31
31
  it 'is invalid if `requests` is an empty array' do
32
- gatherer = described_class.new(server, requests: [])
32
+ gatherer = described_class.new(storage, requests: [])
33
33
  expect(gatherer.valid?).to be false
34
34
  expect(gatherer.errors).to include I18n.t(:requests_empty)
35
35
  end
36
36
 
37
37
  it 'is invalid if `url` missing from any requests' do
38
- gatherer = described_class.new(server, requests: [{ url: 'hello' }, {}])
38
+ gatherer = described_class.new(
39
+ storage, requests: [{ url: 'hello' }, {}]
40
+ )
39
41
  expect(gatherer.valid?).to be false
40
42
  expect(gatherer.errors).to include I18n.t(:missing_url)
41
43
  end
@@ -43,14 +45,29 @@ describe WebFetch::Gatherer do
43
45
 
44
46
  context 'valid' do
45
47
  it 'is valid when passed valid params' do
46
- expect(described_class.new(server, valid_params).valid?).to be true
48
+ expect(described_class.new(storage, valid_params).valid?).to be true
47
49
  end
48
50
  end
49
51
  end
50
52
 
51
53
  describe '#start' do
54
+ let(:http) do
55
+ double(new: double(public_send: double(callback: nil, errback: nil)))
56
+ end
57
+
58
+ let(:logger) { double(debug: nil) }
59
+
60
+ before do
61
+ stub_request(:get, 'http://remotehost:8089/').to_return(status: 200)
62
+ stub_request(:get, 'http://blah/').to_return(status: 200)
63
+ stub_request(:put, 'http://blah/').to_return(status: 200)
64
+ stub_request(:get, 'http://hello/').to_return(status: 200)
65
+ end
66
+
52
67
  it 'returns a hash containing sha1 hashes of requests' do
53
- response = described_class.new(server, valid_params).start
68
+ deferred = double(callback: nil, errback: nil)
69
+ http = double(new: double(public_send: deferred))
70
+ response = described_class.new(storage, valid_params, logger, http).start
54
71
  hash = Digest::SHA1.new.digest(JSON.dump(valid_params[:requests].first))
55
72
  expect(response[:requests].first[:hash]).to eql Digest.hexencode(hash)
56
73
  end
@@ -68,14 +85,16 @@ describe WebFetch::Gatherer do
68
85
  headers: { 'Content-Type' => 'hello' },
69
86
  method: 'PUT' }
70
87
  responses = [req1, req2, req3, req4, req5].map do |req|
71
- described_class.new(server, requests: [req], _server: server).start
88
+ described_class.new(
89
+ storage, { requests: [req] }, logger, http
90
+ ).start
72
91
  end
73
92
  hashes = responses.map { |res| res[:requests].first[:hash] }
74
93
  expect(hashes.uniq.length).to eql 5
75
94
  end
76
95
 
77
96
  it 'returns a hash containing unique IDs for requests' do
78
- response = described_class.new(server, valid_params).start
97
+ response = described_class.new(storage, valid_params, logger, http).start
79
98
  uid1 = response[:requests][0][:uid]
80
99
  uid2 = response[:requests][1][:uid]
81
100
  expect(uid1).to_not eql uid2
@@ -85,9 +104,8 @@ describe WebFetch::Gatherer do
85
104
  it 'is included in response' do
86
105
  # Ensure that the requester can embed their own identifiers to link to
87
106
  # the uid of the delegated request
88
- params = { requests: [url: '-', bob: 'hello'],
89
- _server: server }
90
- response = described_class.new(server, params).start
107
+ params = { requests: [url: 'http://blah/', bob: 'hello'] }
108
+ response = described_class.new(storage, params, logger, http).start
91
109
  expect(response[:requests].first[:request][:bob]).to eql 'hello'
92
110
  end
93
111
 
@@ -95,14 +113,13 @@ describe WebFetch::Gatherer do
95
113
  # Ensure that only pertinent values are used to compute hash (i.e.
96
114
  # adding auxiliary data will still allow retrieval by hash for
97
115
  # otherwise duplicate requests
98
- params1 = { requests: [url: 'http://blah', bob: 'hello'],
99
- _server: server }
100
- response1 = described_class.new(server, params1).start
101
-
102
- params2 = { requests: [url: 'http://blah', not_bob: 'good bye'],
103
- _server: server }
104
- response2 = described_class.new(server, params2).start
105
- expect(response1[:requests][0][:hash]).to eql response2[:requests][0][:hash]
116
+ params1 = { requests: [url: 'http://blah', bob: 'hello'] }
117
+ response1 = described_class.new(storage, params1, logger, http).start
118
+
119
+ params2 = { requests: [url: 'http://blah', not_bob: 'good bye'] }
120
+ response2 = described_class.new(storage, params2, logger, http).start
121
+ expect(response1[:requests][0][:hash])
122
+ .to eql response2[:requests][0][:hash]
106
123
  end
107
124
  end
108
125
  end
@@ -9,21 +9,28 @@ RSpec.describe WebFetch::Promise do
9
9
  let(:find_url) { "http://#{client.host}:#{client.port}/find/#{uid}" }
10
10
 
11
11
  let(:client_success) do
12
- double(fetch: double('success',
13
- complete?: true, pending?: false, success?: true, error: nil
14
- ))
12
+ double(
13
+ fetch: double('success',
14
+ complete?: true,
15
+ pending?: false,
16
+ success?: true,
17
+ error: nil)
18
+ )
15
19
  end
16
20
 
17
21
  let(:client_failure) do
18
- double(fetch: double('failure',
19
- complete?: true, pending?: false, success?: false, error: 'foo'
20
- ))
22
+ double(
23
+ fetch: double('failure',
24
+ complete?: true,
25
+ pending?: false,
26
+ success?: false,
27
+ error: 'foo')
28
+ )
21
29
  end
22
30
 
23
31
  let(:client_pending) do
24
32
  double(fetch: double('pending',
25
- complete?: false, pending?: true, error: nil
26
- ))
33
+ complete?: false, pending?: true, error: nil))
27
34
  end
28
35
 
29
36
  let(:client_not_started) do
@@ -36,8 +43,30 @@ RSpec.describe WebFetch::Promise do
36
43
 
37
44
  describe '#fetch' do
38
45
  before do
39
- stub_request(:get, retrieve_url).to_return(body: { request: {} }.to_json)
40
- stub_request(:get, find_url).to_return(body: { request: {} }.to_json)
46
+ stub_request(:get, retrieve_url)
47
+ .to_return(
48
+ body: {
49
+ request: {
50
+ uid: 123,
51
+ request: {},
52
+ response: {
53
+ success: true, status: 200, body: 'abc123', headers: {}
54
+ }
55
+ }
56
+ }.to_json
57
+ )
58
+
59
+ stub_request(:get, find_url).to_return(
60
+ body: {
61
+ request: {
62
+ uid: 123,
63
+ request: {},
64
+ response: {
65
+ success: true, status: 200, body: 'abc123', headers: {}
66
+ }
67
+ }
68
+ }.to_json
69
+ )
41
70
  end
42
71
 
43
72
  subject { promise.fetch(fetch_options) }
@@ -63,7 +92,16 @@ RSpec.describe WebFetch::Promise do
63
92
  stub_request(:get, retrieve_url)
64
93
  .to_return(
65
94
  body: {
66
- response: { success: true, body: 'abc123' }, request: {}
95
+ request: {
96
+ uid: 123,
97
+ request: {},
98
+ response: {
99
+ success: true,
100
+ status: 200,
101
+ body: Base64.encode64('abc123'),
102
+ headers: {}
103
+ }
104
+ }
67
105
  }.to_json
68
106
  )
69
107
  promise.fetch
@@ -6,11 +6,16 @@ describe WebFetch::Resources do
6
6
  describe '.root' do
7
7
  it 'responds with application name' do
8
8
  expect(described_class.root(nil, nil))
9
- .to eql(status: 200, payload: { application: 'WebFetch' })
9
+ .to eql(
10
+ status: 200,
11
+ command: 'root',
12
+ payload: { application: 'WebFetch' }
13
+ )
10
14
  end
11
15
  end
12
16
 
13
17
  describe '.gather' do
18
+ before { stub_request(:any, 'http://google.com').to_return(status: 200) }
14
19
  let(:response) do
15
20
  described_class.gather(server, requests: [{ url: 'http://google.com' }])
16
21
  end
@@ -25,34 +30,26 @@ describe WebFetch::Resources do
25
30
  end
26
31
 
27
32
  describe '.retrieve' do
28
- it 'gives 404 not found when unrecognised uid requested' do
33
+ it 'gives pending when unrecognised uid requested' do
29
34
  response = described_class.retrieve(server, uid: '123')
30
- expect(response[:status]).to eql 404
31
- error = response[:payload][:error]
32
- expect(error).to eql I18n.t(:uid_not_found)
35
+ expect(response[:request][:pending]).to be true
33
36
  end
34
37
 
35
- it 'gives 404 not found when unrecognised hash requested' do
38
+ it 'gives pending when unrecognised hash requested' do
36
39
  response = described_class.retrieve(server, hash: 'abc')
37
- expect(response[:status]).to eql 404
38
- error = response[:payload][:error]
39
- expect(error).to eql I18n.t(:hash_not_found)
40
+ expect(response[:request][:pending]).to be true
40
41
  end
41
42
  end
42
43
 
43
44
  describe '.find' do
44
- it 'gives 404 not found when unrecognised uid requested' do
45
+ it 'gives pending when unrecognised uid requested' do
45
46
  response = described_class.find(server, uid: '123')
46
- expect(response[:status]).to eql 404
47
- error = response[:payload][:error]
48
- expect(error).to eql I18n.t(:uid_not_found)
47
+ expect(response[:request][:pending]).to be true
49
48
  end
50
49
 
51
- it 'gives 404 not found when unrecognised hash requested' do
50
+ it 'gives pending when unrecognised hash requested' do
52
51
  response = described_class.find(server, hash: 'abc')
53
- expect(response[:status]).to eql 404
54
- error = response[:payload][:error]
55
- expect(error).to eql I18n.t(:hash_not_found)
52
+ expect(response[:request][:pending]).to be true
56
53
  end
57
54
  end
58
55
  end
@@ -3,15 +3,19 @@
3
3
  RSpec.describe WebFetch::Response do
4
4
  let(:response) do
5
5
  described_class.new(
6
- body: 'abc123',
7
- headers: { 'Foo' => 'Bar' },
8
- status: 200,
9
- pending: false,
10
- success: false,
11
- error: 'foo error happened',
12
- request: { url: 'http://blah/' },
13
- uid: 'uid123',
14
- response_time: 123.45
6
+ request: {
7
+ response: {
8
+ body: Base64.encode64('abc123'),
9
+ headers: { 'Foo' => 'Bar' },
10
+ error: 'foo error happened',
11
+ status: 200,
12
+ success: false
13
+ },
14
+ pending: false,
15
+ request: { url: 'http://blah/' },
16
+ uid: 'uid123',
17
+ response_time: 123.45
18
+ }
15
19
  )
16
20
  end
17
21
 
@@ -1,23 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  describe WebFetch::Retriever do
4
- let(:server) { WebFetch::MockServer.new }
4
+ let(:storage) { double(fetch: nil, store: nil) }
5
5
  let(:valid_params) { { uid: 'abc123' } }
6
6
 
7
7
  it 'is initialisable with params' do
8
- expect(described_class.new(server, valid_params, {}))
8
+ expect(described_class.new(storage, valid_params, {}))
9
9
  .to be_a described_class
10
10
  end
11
11
 
12
12
  describe 'validation' do
13
13
  context 'valid' do
14
14
  it 'is valid when `uid` given' do
15
- retriever = described_class.new(server, { uid: 'abc123' }, {})
15
+ retriever = described_class.new(storage, { uid: 'abc123' }, {})
16
16
  expect(retriever.valid?).to be true
17
17
  end
18
18
 
19
19
  it 'is valid when `hash` given' do
20
- retriever = described_class.new(server, { hash: 'def456' }, {})
20
+ retriever = described_class.new(storage, { hash: 'def456' }, {})
21
21
  expect(retriever.valid?).to be true
22
22
  end
23
23
  end
@@ -25,14 +25,14 @@ describe WebFetch::Retriever do
25
25
  context 'invalid' do
26
26
  it 'is invalid if both `hash` and `uid` given' do
27
27
  retriever = described_class.new(
28
- server, { hash: 'def456', uid: 'abc123' }, {}
28
+ storage, { hash: 'def456', uid: 'abc123' }, {}
29
29
  )
30
30
  expect(retriever.valid?).to be false
31
31
  expect(retriever.errors).to include I18n.t(:hash_or_uid_but_not_both)
32
32
  end
33
33
 
34
34
  it 'is invalid if neither `hash` nor `uid` given' do
35
- retriever = described_class.new(server, {}, {})
35
+ retriever = described_class.new(storage, {}, {})
36
36
  expect(retriever.valid?).to be false
37
37
  expect(retriever.errors).to include I18n.t(:missing_hash_and_uid)
38
38
  end
@@ -40,14 +40,14 @@ describe WebFetch::Retriever do
40
40
  end
41
41
 
42
42
  describe '#find' do
43
- it 'returns `nil` when given uid has not been requested' do
44
- retriever = described_class.new(server, { uid: 'nope' }, {})
45
- expect(retriever.find).to be_nil
43
+ it 'returns "pending" when given uid has not been requested' do
44
+ retriever = described_class.new(storage, { uid: 'nope' }, {})
45
+ expect(retriever.find[:pending]).to be true
46
46
  end
47
47
 
48
- it 'returns `nil` when given hash has not been requested' do
49
- retriever = described_class.new(server, { hash: 'also nope' }, {})
50
- expect(retriever.find).to be_nil
48
+ it 'returns "pending" when given hash has not been requested' do
49
+ retriever = described_class.new(storage, { hash: 'also nope' }, {})
50
+ expect(retriever.find[:pending]).to be true
51
51
  end
52
52
 
53
53
  it 'returns payload when request has been retrieved' do
@@ -58,14 +58,13 @@ describe WebFetch::Retriever do
58
58
  url = 'http://blah.blah/success'
59
59
  stub_request(:any, url)
60
60
 
61
- gatherer = WebFetch::Gatherer.new(server, requests: [{ url: url }])
61
+ gatherer = WebFetch::Gatherer.new(storage, requests: [{ url: url }])
62
62
  response = gatherer.start
63
63
  uid = response[:requests].first[:uid]
64
- expect(server).to receive(:storage)
65
- .and_return(uid => { body: 'fake body' })
64
+ allow(storage).to receive(:fetch).and_return('test')
66
65
 
67
- retriever = described_class.new(server, { uid: uid }, {})
68
- expect(retriever.find[:body]).to eql 'fake body'
66
+ retriever = described_class.new(storage, { uid: uid }, {})
67
+ expect(retriever.find).to eql 'test'
69
68
  end
70
69
  end
71
70
  end