harpy 1.1.0 → 2.0.0.preview1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6437acd381d436843e94394aa9d1c6df605c4ca43ca5fb3e32746a4f6d805ed
4
- data.tar.gz: b03c709ef5b4297a059280926773fb376b5f9feb77ab6656ea9f7d169cd69c6b
3
+ metadata.gz: c6bccc44b8e3516964d14dfb259eb7b9294c42449c4f9f72876b5ed1852ed3d0
4
+ data.tar.gz: 989d3494a0b11a5ee04b2c472c2f3ac85920bf42296c3194c1a756f7e1ddd680
5
5
  SHA512:
6
- metadata.gz: 074b8e393b71cb510d5f8acf96aa2473395689458d4b7b1ca58965b44a1a902be536f77f452a0bd1d5d675b456244d52cf0937b1c266abdc019f16eaed71b475
7
- data.tar.gz: 96996c71087173593a8eaed967fdd500a505ab92b6ddcf0e2025cb62ab87735a29748e9e8958ed37573e74ea96c3bdfb8c4afabe5de4865fd5316598c19b705d
6
+ metadata.gz: c0d99509101d673e64f08a18d7dd0ac8335f59e462e60e51638e365fc54b5a11bb5cd43e1454052af17bab69ebb7a94b6b69dbd0de27a5d8932f694a90e61c5b
7
+ data.tar.gz: 83f63ada339c06bd86231d8175b767bf5e56afffd80d7862093189a28e2eb4c86cdf8243b5cd88113fca49715f8f6a94bda439983c024cbef383a171a5ee48d3
@@ -0,0 +1,33 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: docker
10
+ container:
11
+ image: docker.io/library/ruby:${{ matrix.image }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ include:
16
+ - ruby: "3.4"
17
+ image: "3.4"
18
+ - ruby: "4.0"
19
+ image: "4.0"
20
+ - ruby: "4.1"
21
+ image: "4.1-rc"
22
+ steps:
23
+ - name: Install Node.js 24 for the checkout action
24
+ run: |
25
+ apt-get update
26
+ apt-get install -y ca-certificates curl
27
+ curl -fsSL https://deb.nodesource.com/setup_24.x | bash -
28
+ apt-get install -y nodejs
29
+ node --version
30
+ - name: Check out the repository
31
+ uses: actions/checkout@v7
32
+ - run: bundle install
33
+ - run: bundle exec rake
@@ -0,0 +1,20 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ ruby: ["3.4", "4.0", "4.1"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: ${{ matrix.ruby }}
19
+ bundler-cache: true
20
+ - run: bundle exec rake
data/README.md CHANGED
@@ -1,16 +1,15 @@
1
1
  Harpy
2
2
  ======
3
3
 
4
- [![Build Status](https://travis-ci.org/TalentBox/harpy.png?branch=master)](https://travis-ci.org/TalentBox/harpy)
5
- [![Code Climate](https://codeclimate.com/github/TalentBox/harpy.png)](https://codeclimate.com/github/TalentBox/harpy)
4
+ [![Test](https://github.com/TalentBox/harpy/actions/workflows/test.yml/badge.svg)](https://github.com/TalentBox/harpy/actions/workflows/test.yml)
6
5
 
7
6
  Client for REST API with HATEOAS
8
7
 
9
8
  Dependencies
10
9
  ------------
11
10
 
12
- * Ruby >= 2.4.
13
- * gem "typhoeus", ">= 0.6.5"
11
+ * Ruby >= 3.4.
12
+ * gem "httpx", ">= 1.0"
14
13
  * gem "activesupport", ">= 5.2.0"
15
14
  * gem "activemodel", ">= 5.2.0"
16
15
  * gem "hash-deep-merge", ">= 0.1.1"
@@ -44,7 +43,7 @@ Usage
44
43
  # => instance of MyModel with attributes filled in on 200
45
44
  # => nil on 404
46
45
  # => raises Harpy::ClientTimeout on timeout
47
- # => raises Harpy::ClientError on Curl error
46
+ # => raises Harpy::ClientError on HTTP client error
48
47
  # => raises Harpy::InvalidResponseCode on other response codes
49
48
 
50
49
  # Fetch multiple by url in parallel
@@ -60,7 +59,7 @@ Usage
60
59
  }
61
60
  # => return an array of MyModel instances on 200
62
61
  # => raises Harpy::ClientTimeout on timeout
63
- # => raises Harpy::ClientError on Curl error
62
+ # => raises Harpy::ClientError on HTTP client error
64
63
  # => raises Harpy::InvalidResponseCode on other response codes
65
64
 
66
65
  # Search by first_name
data/harpy.gemspec CHANGED
@@ -12,17 +12,19 @@ Gem::Specification.new do |s|
12
12
  s.summary = %q{Client for REST API}
13
13
  s.description = %q{Client for REST API with HATEOAS}
14
14
  s.license = "MIT"
15
+ s.required_ruby_version = ">= 3.4"
15
16
 
16
17
  s.files = `git ls-files`.split("\n")
17
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
20
  s.require_paths = ["lib"]
20
21
 
21
- s.add_runtime_dependency "typhoeus", ">= 0.6.5"
22
+ s.add_runtime_dependency "httpx", ">= 1.0"
22
23
  s.add_runtime_dependency "activesupport", [">= 5.2.0"]
23
24
  s.add_runtime_dependency "activemodel", [">= 5.2.0"]
24
25
  s.add_runtime_dependency "hash-deep-merge", [">= 0.1.1"]
25
26
 
26
27
  s.add_development_dependency "rake", [">= 0.8.7"]
27
28
  s.add_development_dependency "rspec"
29
+ s.add_development_dependency "webmock"
28
30
  end
data/lib/harpy/client.rb CHANGED
@@ -1,4 +1,6 @@
1
- require "typhoeus"
1
+ require "base64"
2
+ require "openssl"
3
+ require "httpx"
2
4
  require "hash_deep_merge"
3
5
 
4
6
  module Harpy
@@ -7,6 +9,7 @@ module Harpy
7
9
 
8
10
  def initialize(opts=nil)
9
11
  self.options = (opts || {})
12
+ @session = HTTPX::Session.new
10
13
  end
11
14
 
12
15
  def get(url_or_urls, opts=nil)
@@ -34,33 +37,68 @@ module Harpy
34
37
  end
35
38
 
36
39
  def run(requests)
37
- requests.each{|request| Typhoeus::Hydra.hydra.queue request}
38
- Typhoeus::Hydra.hydra.run
39
- requests.collect(&:response)
40
+ return [] if requests.empty?
41
+
42
+ responses = @session.request(*requests)
43
+ responses.is_a?(Array) ? responses : [responses]
44
+ end
45
+
46
+ def response_status(response)
47
+ response.is_a?(HTTPX::ErrorResponse) ? 0 : response.status
40
48
  end
41
49
 
42
50
  def invalid_code(response)
43
- if response.timed_out?
44
- raise ClientTimeout
45
- elsif response.code.zero?
46
- raise ClientError, response.return_message
51
+ if response.is_a?(HTTPX::ErrorResponse)
52
+ if response.error.is_a?(HTTPX::TimeoutError)
53
+ raise ClientTimeout
54
+ else
55
+ raise ClientError, response.error.message
56
+ end
47
57
  else
48
- raise InvalidResponseCode, response.code.to_s
58
+ raise InvalidResponseCode, response.status.to_s
49
59
  end
50
60
  end
51
61
 
52
62
  private
53
63
 
54
64
  def request(method, urls, opts=nil)
55
- opts = options.deep_merge(opts || {})
65
+ opts = httpx_options(options.deep_merge(opts || {}))
56
66
  case urls
57
67
  when Array
58
- requests = urls.collect do |url|
59
- Typhoeus::Request.new url, opts.merge(:method => method)
68
+ urls.collect do |url|
69
+ @session.build_request(method, url, opts.dup)
60
70
  end
61
71
  else
62
- Typhoeus::Request.send method, urls, opts
72
+ @session.request(method.to_s.upcase, urls, **opts)
73
+ end
74
+ end
75
+
76
+ def httpx_options(opts)
77
+ timeout = opts[:timeout]
78
+ if timeout.is_a?(Numeric)
79
+ opts[:timeout] = {total_request_timeout: timeout.to_f / 1000} if timeout.positive?
80
+ opts.delete(:timeout) unless timeout.positive?
81
+ end
82
+
83
+ ssl_options = (opts[:ssl] || {}).dup
84
+ if opts.key?(:ssl_verifypeer)
85
+ verify_peer = opts.delete(:ssl_verifypeer)
86
+ ssl_options[:verify_mode] = verify_peer ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
87
+ end
88
+ if opts.key?(:ssl_verifyhost)
89
+ verify_host = opts.delete(:ssl_verifyhost)
90
+ ssl_options[:verify_hostname] = verify_host != false && verify_host != 0
91
+ end
92
+ opts[:ssl] = ssl_options unless ssl_options.empty?
93
+
94
+ username = opts.delete(:username)
95
+ password = opts.delete(:password)
96
+ if username || password
97
+ headers = (opts[:headers] || {}).dup
98
+ headers["Authorization"] ||= "Basic #{Base64.strict_encode64("#{username}:#{password}")}"
99
+ opts[:headers] = headers
63
100
  end
101
+ opts
64
102
  end
65
103
  end
66
104
  end
@@ -10,7 +10,7 @@ module Harpy
10
10
 
11
11
  def resource_url(resource_type)
12
12
  response = Harpy.client.get url
13
- case response.code
13
+ case Harpy.client.response_status(response)
14
14
  when 200
15
15
  body = JSON.parse response.body
16
16
  link = (body["link"] || []).detect{|link| link["rel"] == resource_type}
@@ -22,9 +22,9 @@ module Harpy
22
22
 
23
23
  def urn(urn)
24
24
  response = Harpy.client.get "#{url}/#{urn}"
25
- case response.code
25
+ case Harpy.client.response_status(response)
26
26
  when 301
27
- response.headers_hash["Location"]
27
+ response.headers["location"]
28
28
  when 404
29
29
  nil
30
30
  else
@@ -4,10 +4,10 @@ module Harpy
4
4
  end
5
5
 
6
6
  module VERSION
7
- MAJOR = 1
8
- MINOR = 1
7
+ MAJOR = 2
8
+ MINOR = 0
9
9
  TINY = 0
10
- PRE = nil
10
+ PRE = "preview1"
11
11
 
12
12
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
13
13
  end
@@ -62,7 +62,7 @@ module Harpy
62
62
 
63
63
  def search(conditions={})
64
64
  response = Harpy.client.get url, :params => conditions
65
- case response.code
65
+ case Harpy.client.response_status(response)
66
66
  when 200
67
67
  parsed = JSON.parse response.body
68
68
  items = parsed[resource_name].collect{|model| new model}
@@ -109,7 +109,7 @@ module Harpy
109
109
  end
110
110
 
111
111
  def from_url_handler(response)
112
- case response.code
112
+ case Harpy.client.response_status(response)
113
113
  when 200
114
114
  new JSON.parse response.body
115
115
  when 404
@@ -120,7 +120,7 @@ module Harpy
120
120
  end
121
121
 
122
122
  def delete_from_url_handler(response)
123
- case response.code
123
+ case Harpy.client.response_status(response)
124
124
  when 204
125
125
  true
126
126
  when 404
@@ -228,7 +228,7 @@ module Harpy
228
228
  end
229
229
 
230
230
  def process_response(response, context)
231
- case response.code
231
+ case Harpy.client.response_status(response)
232
232
  when 200, 201, 302
233
233
  @attrs.merge! JSON.parse(response.body)
234
234
  true
@@ -249,6 +249,7 @@ module Harpy
249
249
  def read_attribute_for_validation(key)
250
250
  @attrs[key]
251
251
  end
252
+ public :read_attribute_for_validation
252
253
 
253
254
  def method_missing(method, *args)
254
255
  key = method.to_s
@@ -25,78 +25,120 @@ describe Harpy::Client do
25
25
  describe "##{method}(url, opts={})" do
26
26
  context "with one url" do
27
27
  before do
28
- @expected = Typhoeus::Response.new :code => 200
29
- Typhoeus.stub(entry_url, method: method){@expected}
28
+ stub_request(method, entry_url).to_return(status: 200)
30
29
  end
30
+
31
31
  it "sends a #{method.to_s.upcase} to the url" do
32
- expect(subject.send(method, entry_url)).to eq(@expected)
32
+ response = subject.send(method, entry_url)
33
+ expect(response.status).to eq(200)
34
+ expect(WebMock).to have_requested(method, entry_url)
33
35
  end
36
+
34
37
  it "merges options" do
35
38
  client = Harpy::Client.new :headers => {"Authorization" => "spec"}
36
- Typhoeus.stub(entry_url, method: method){Typhoeus::Response.new :code => 200}
39
+ stub_request(method, entry_url).to_return(status: 200)
37
40
  response = client.send method, entry_url, :headers => {"X-Files" => "Harpy"}
38
- expect(response.request.options[:headers]).to include({"X-Files" => "Harpy", "Authorization" => "spec"})
41
+ expect(WebMock).to have_requested(method, entry_url).with(headers: {
42
+ "X-Files" => "Harpy",
43
+ "Authorization" => "spec"
44
+ })
39
45
  end
40
46
  end
47
+
41
48
  context "with multiple urls" do
42
49
  it "does not execute requests" do
43
50
  expect {
44
51
  subject.send method, [entry_url, users_url]
45
52
  }.not_to raise_error
46
53
  end
47
- it "returns one requests per url" do
54
+
55
+ it "returns one request per url" do
48
56
  requests = subject.send method, [entry_url, users_url]
49
57
  expect(requests.size).to eq(2)
50
- expect(requests.collect{|r| r.options[:method]}).to match_array([method, method])
51
- expect(requests.collect(&:url)).to match_array([entry_url, users_url])
58
+ expect(requests.collect{|request| request.verb.downcase.to_sym}).to match_array([method, method])
59
+ expect(requests.collect{|request| request.uri.to_s}).to match_array([entry_url, users_url])
52
60
  end
61
+
53
62
  it "merges options" do
54
63
  client = Harpy::Client.new :headers => {"Authorization" => "spec"}
55
64
  requests = client.send method, [entry_url, users_url], :headers => {"X-Files" => "Harpy"}
56
65
  requests.each do |request|
57
- expect(request.options[:headers]).to include({"X-Files" => "Harpy", "Authorization" => "spec"})
66
+ expect(request.headers["x-files"]).to eq("Harpy")
67
+ expect(request.headers["authorization"]).to eq("spec")
58
68
  end
59
69
  end
60
70
  end
61
71
  end
62
72
  end
73
+
74
+ describe "HTTPX request options" do
75
+ it "encodes query parameters" do
76
+ stub_request(:get, "#{users_url}?active=true").to_return(status: 200)
77
+
78
+ response = subject.get(users_url, params: {active: true})
79
+
80
+ expect(response.status).to eq(200)
81
+ expect(WebMock).to have_requested(:get, "#{users_url}?active=true")
82
+ end
83
+
84
+ it "maps username and password to basic authentication" do
85
+ stub_request(:get, entry_url).with(headers: {"Authorization" => "Basic aGFycHk6c3BlYw=="}).to_return(status: 200)
86
+
87
+ response = Harpy::Client.new(username: "harpy", password: "spec").get(entry_url)
88
+
89
+ expect(response.status).to eq(200)
90
+ end
91
+
92
+ it "maps legacy timeout and SSL options" do
93
+ request = Harpy::Client.new(timeout: 5000, ssl_verifypeer: false, ssl_verifyhost: 0).get([entry_url]).first
94
+
95
+ expect(request.options.timeout.total_request_timeout).to eq(5)
96
+ expect(request.options.ssl).to include(verify_mode: OpenSSL::SSL::VERIFY_NONE, verify_hostname: false)
97
+ end
98
+ end
99
+
63
100
  describe "#run(requests)" do
64
101
  before do
65
- @entry_response = Typhoeus::Response.new :code => 200, :body => "entry"
66
- @users_response = Typhoeus::Response.new :code => 200, :body => "users"
67
-
68
- Typhoeus.stub(entry_url, method: :get){ @entry_response }
69
- Typhoeus.stub(users_url, method: :get){ @users_response }
102
+ stub_request(:get, entry_url).to_return(status: 200, body: "entry")
103
+ stub_request(:get, users_url).to_return(status: 200, body: "users")
70
104
  end
71
- it "executes requests in parallel" do
72
- expect(Typhoeus::Hydra.hydra).to receive(:run).once
73
- subject.run subject.get([entry_url, users_url])
105
+
106
+ it "submits the requests together to HTTPX" do
107
+ requests = subject.get([entry_url, users_url])
108
+ expect(subject.instance_variable_get(:@session)).to receive(:request).with(*requests).and_call_original
109
+ subject.run requests
74
110
  end
111
+
75
112
  it "returns responses" do
76
113
  responses = subject.run subject.get([entry_url, users_url])
77
- expect(responses).to match_array([@entry_response, @users_response])
114
+ expect(responses.map(&:body)).to match_array(["entry", "users"])
78
115
  end
79
- it "requests response is filled in" do
116
+
117
+ it "fills in each request response" do
80
118
  requests = subject.get([entry_url, users_url])
81
119
  subject.run requests
82
- expect(requests[0].response).to eq(@entry_response)
83
- expect(requests[1].response).to eq(@users_response)
120
+ expect(requests.map{|request| request.response.body}).to eq(["entry", "users"])
84
121
  end
85
122
  end
123
+
86
124
  describe "#invalid_code(response)" do
87
125
  it "raises Harpy::ClientTimeout on request timeout" do
126
+ error = HTTPX::RequestTimeoutError.new(nil, nil, 1)
88
127
  expect {
89
- subject.invalid_code double("Response", :timed_out? => true)
128
+ subject.invalid_code httpx_error_response(error)
90
129
  }.to raise_error Harpy::ClientTimeout
91
130
  end
92
- it "raises Harpy::ClientError on code 0" do
131
+
132
+ it "raises Harpy::ClientError on transport error" do
133
+ error = HTTPX::ConnectionError.new("Could not connect to server")
93
134
  expect {
94
- subject.invalid_code double("Response", :timed_out? => false, :code => 0, :return_message => "Could not connect to server")
135
+ subject.invalid_code httpx_error_response(error)
95
136
  }.to raise_error Harpy::ClientError, "Could not connect to server"
96
137
  end
138
+
97
139
  it "raises Harpy::InvalidResponseCode with code otherwise" do
98
140
  expect {
99
- subject.invalid_code double("Response", :timed_out? => false, :code => 404)
141
+ subject.invalid_code httpx_response(:code => 404)
100
142
  }.to raise_error Harpy::InvalidResponseCode, "404"
101
143
  end
102
144
  end
@@ -11,8 +11,8 @@ describe Harpy::EntryPoint do
11
11
  describe "#resource_url(resource_type)" do
12
12
  let(:company_url) { "#{url}/company"}
13
13
  let(:json_response) { %Q|{"link": [{"rel": "company", "href": "#{company_url}"}]}| }
14
- let(:success_response) { Typhoeus::Response.new :code => 200, :body => json_response }
15
- let(:error_response) { Typhoeus::Response.new :code => 500 }
14
+ let(:success_response) { httpx_response :code => 200, :body => json_response }
15
+ let(:error_response) { httpx_response :code => 500 }
16
16
  it "gets entry point from url using client" do
17
17
  expect(Harpy.client).to receive(:get).with(url).and_return success_response
18
18
  subject.resource_url "user"
@@ -35,9 +35,9 @@ describe Harpy::EntryPoint do
35
35
  describe "#urn(urn)" do
36
36
  let(:urn) { "urn:harpy:company:1" }
37
37
  let(:company_url) { "#{url}/company/1"}
38
- let(:success_response) { Typhoeus::Response.new :code => 301, :headers => "Location: #{company_url}" }
39
- let(:error_response) { Typhoeus::Response.new :code => 500 }
40
- let(:not_found_response) { Typhoeus::Response.new :code => 404 }
38
+ let(:success_response) { httpx_response :code => 301, :headers => "Location: #{company_url}" }
39
+ let(:error_response) { httpx_response :code => 500 }
40
+ let(:not_found_response) { httpx_response :code => 404 }
41
41
  it "query remote for this urn using client" do
42
42
  expect(Harpy.client).to receive(:get).with("#{url}/#{urn}").and_return success_response
43
43
  expect(subject.urn(urn)).to eq(company_url)
@@ -4,9 +4,8 @@ describe Harpy::Resource do
4
4
  describe ".from_url(hash)" do
5
5
  let(:company_url) { "http://localhost/company/1" }
6
6
  let(:json_response) { %Q|{"name": "Harpy ltd", "link": [{"rel": "self", "href": "#{company_url}"}]}| }
7
- let(:success_response) { Typhoeus::Response.new :code => 200, :body => json_response}
8
7
  it "queries multiple resources in parallel and return instances" do
9
- Typhoeus.stub(company_url, method: :get){ success_response }
8
+ stub_request(:get, company_url).to_return(status: 200, body: json_response)
10
9
  responses = Harpy::Resource.from_url({ Harpy::Spec::Company => [company_url] })
11
10
  expect(responses.size).to eq(1)
12
11
  expect(responses[Harpy::Spec::Company].size).to eq(1)
@@ -59,7 +58,7 @@ describe "class including Harpy::Resource" do
59
58
  context "called with only one url" do
60
59
  let(:url){ "http://localhost/company/1" }
61
60
  it "is a properly filled-in instance of Harpy::Spec::Company on success" do
62
- response = Typhoeus::Response.new :code => 200, :body => <<-eos
61
+ response = httpx_response :code => 200, :body => <<-eos
63
62
  {
64
63
  "name": "Harpy Ltd",
65
64
  "link": [
@@ -74,12 +73,12 @@ describe "class including Harpy::Resource" do
74
73
  expect(result.link("self")).to eq(url)
75
74
  end
76
75
  it "is nil when not found" do
77
- response = Typhoeus::Response.new :code => 404
76
+ response = httpx_response :code => 404
78
77
  expect(Harpy.client).to receive(:get).with(url).and_return response
79
78
  expect(Harpy::Spec::Company.from_url(url)).to be_nil
80
79
  end
81
80
  it "delegates response code != 200 or 404 to client" do
82
- response = Typhoeus::Response.new :code => 500
81
+ response = httpx_response :code => 500
83
82
  expect(Harpy.client).to receive(:get).with(url).and_return response
84
83
  expect(Harpy.client).to receive(:invalid_code).with response
85
84
  Harpy::Spec::Company.from_url(url)
@@ -89,7 +88,7 @@ describe "class including Harpy::Resource" do
89
88
  let(:url1){ "http://localhost/company/1" }
90
89
  let(:url2){ "http://localhost/company/2" }
91
90
  it "is a properly filled-in instance of Harpy::Spec::Company on success" do
92
- response1 = Typhoeus::Response.new :code => 200, :body => <<-eos
91
+ response1 = httpx_response :code => 200, :body => <<-eos
93
92
  {
94
93
  "name": "Harpy Ltd",
95
94
  "link": [
@@ -97,7 +96,7 @@ describe "class including Harpy::Resource" do
97
96
  ]
98
97
  }
99
98
  eos
100
- response2 = Typhoeus::Response.new :code => 200, :body => <<-eos
99
+ response2 = httpx_response :code => 200, :body => <<-eos
101
100
  {
102
101
  "name": "Harpy Inc",
103
102
  "link": [
@@ -105,8 +104,8 @@ describe "class including Harpy::Resource" do
105
104
  ]
106
105
  }
107
106
  eos
108
- Typhoeus.stub(url1, method: :get){ response1 }
109
- Typhoeus.stub(url2, method: :get){ response2 }
107
+ stub_request(:get, url1).to_return(status: 200, body: response1.body.to_s)
108
+ stub_request(:get, url2).to_return(status: 200, body: response2.body.to_s)
110
109
  results = Harpy::Spec::Company.from_url [url1, url2]
111
110
  expect(results.size).to eq(2)
112
111
  expect(results[0]).to be_kind_of Harpy::Spec::Company
@@ -197,18 +196,18 @@ describe "class including Harpy::Resource" do
197
196
  context "called with only one url" do
198
197
  let(:url){ "http://localhost/company/1" }
199
198
  it "is true on success" do
200
- response = Typhoeus::Response.new :code => 204
199
+ response = httpx_response :code => 204
201
200
  expect(Harpy.client).to receive(:delete).with(url).and_return response
202
201
  result = Harpy::Spec::Company.delete_from_url url
203
202
  expect(result).to be_truthy
204
203
  end
205
204
  it "is false when not found" do
206
- response = Typhoeus::Response.new :code => 404
205
+ response = httpx_response :code => 404
207
206
  expect(Harpy.client).to receive(:delete).with(url).and_return response
208
207
  expect(Harpy::Spec::Company.delete_from_url(url)).to be_falsey
209
208
  end
210
209
  it "delegates response code != 204 or 404 to client" do
211
- response = Typhoeus::Response.new :code => 500
210
+ response = httpx_response :code => 500
212
211
  expect(Harpy.client).to receive(:delete).with(url).and_return response
213
212
  expect(Harpy.client).to receive(:invalid_code).with response
214
213
  Harpy::Spec::Company.delete_from_url url
@@ -309,7 +308,7 @@ describe "class including Harpy::Resource" do
309
308
  expect(Harpy.entry_point).to receive(:resource_url).with("harpy/spec/company").and_return url
310
309
  end
311
310
  it "return properly filled instances on 200" do
312
- response = Typhoeus::Response.new :code => 200, :body => <<-eos
311
+ response = httpx_response :code => 200, :body => <<-eos
313
312
  {
314
313
  "harpy/spec/company": [
315
314
  {
@@ -347,7 +346,7 @@ describe "class including Harpy::Resource" do
347
346
  expect(companies.blank?).to be true
348
347
  end
349
348
  it "delegates other response codes to client" do
350
- response = Typhoeus::Response.new :code => 500
349
+ response = httpx_response :code => 500
351
350
  expect(Harpy.client).to receive(:get).with(url, :params => {}).and_return response
352
351
  expect(Harpy.client).to receive(:invalid_code).with response
353
352
  Harpy::Spec::Company.search
@@ -356,7 +355,7 @@ describe "class including Harpy::Resource" do
356
355
  describe ".with_url(url)" do
357
356
  let(:url){ "http://localhost/user/1/company" }
358
357
  it "overrides url used for searches" do
359
- response = Typhoeus::Response.new :code => 500
358
+ response = httpx_response :code => 500
360
359
  expect(Harpy.client).to receive(:get).with(url, :params => {}).and_return response
361
360
  expect(Harpy.client).to receive(:invalid_code).with response
362
361
  Harpy::Spec::Company.with_url(url) do
@@ -365,7 +364,7 @@ describe "class including Harpy::Resource" do
365
364
  end
366
365
  it "can be nested" do
367
366
  url2 = "http://localhost/user/2/company"
368
- response = Typhoeus::Response.new :code => 500
367
+ response = httpx_response :code => 500
369
368
  expect(Harpy.client).to receive(:get).ordered.with(url2, :params => {}).and_return response
370
369
  expect(Harpy.client).to receive(:get).ordered.with(url, :params => {}).and_return response
371
370
  expect(Harpy.client).to receive(:invalid_code).twice.with response
@@ -492,7 +491,7 @@ describe "class including Harpy::Resource" do
492
491
  describe "#inspect" do
493
492
  subject { Harpy::Spec::Company.new "firstname" => "Anthony" }
494
493
  it "shows class name, attributes, errors and persisted state" do
495
- expect(subject.inspect).to eq('<Harpy::Spec::Company @attrs:{"firstname"=>"Anthony"} @errors:[] persisted:false>')
494
+ expect(subject.inspect).to include("Harpy::Spec::Company", "firstname", "Anthony", "@errors:[] persisted:false>")
496
495
  end
497
496
  end
498
497
  describe "#has_key?(key)" do
@@ -566,7 +565,7 @@ describe "class including Harpy::Resource" do
566
565
  end
567
566
  [200, 201, 302].each do |response_code|
568
567
  it "is true and merges response attributes on #{response_code}" do
569
- response = Typhoeus::Response.new :code => response_code, :body => <<-eos
568
+ response = httpx_response :code => response_code, :body => <<-eos
570
569
  {
571
570
  "firstname": "Anthony",
572
571
  "urn": "urn:harpy:user:1",
@@ -584,19 +583,19 @@ describe "class including Harpy::Resource" do
584
583
  end
585
584
  end
586
585
  it "raises Harpy::InvalidResponseCode on 204" do
587
- response = Typhoeus::Response.new :code => 204
586
+ response = httpx_response :code => 204
588
587
  expect(Harpy.client).to receive(:post).with(url, :body => body).and_return response
589
588
  expect { subject.save }.to raise_error Harpy::InvalidResponseCode, "204"
590
589
  expect(subject.callbacks).to match_array([:save, :create])
591
590
  end
592
591
  it "raises Harpy::Unauthorized on 401" do
593
- response = Typhoeus::Response.new :code => 401
592
+ response = httpx_response :code => 401
594
593
  expect(Harpy.client).to receive(:post).with(url, :body => body).and_return response
595
594
  expect { subject.save }.to raise_error Harpy::Unauthorized, "Server returned a 401 response code"
596
595
  expect(subject.callbacks).to match_array([:save, :create])
597
596
  end
598
597
  it "is false and fills in errors on 422" do
599
- response = Typhoeus::Response.new :code => 422, :body => <<-eos
598
+ response = httpx_response :code => 422, :body => <<-eos
600
599
  {
601
600
  "firstname": "Anthony",
602
601
  "errors": {
@@ -613,7 +612,7 @@ describe "class including Harpy::Resource" do
613
612
  expect(subject.company_name).to eq("Stark Enterprises")
614
613
  end
615
614
  it "delegates other response codes to client" do
616
- response = Typhoeus::Response.new :code => 500
615
+ response = httpx_response :code => 500
617
616
  expect(Harpy.client).to receive(:post).with(url, :body => body).and_return response
618
617
  expect(Harpy.client).to receive(:invalid_code).with(response)
619
618
  subject.save
@@ -643,7 +642,7 @@ describe "class including Harpy::Resource" do
643
642
  end
644
643
  [200, 201, 302].each do |response_code|
645
644
  it "is true and merges response attributes on #{response_code}" do
646
- response = Typhoeus::Response.new :code => response_code, :body => <<-eos
645
+ response = httpx_response :code => response_code, :body => <<-eos
647
646
  {
648
647
  "firstname": "Anthony",
649
648
  "urn": "urn:harpy:user:1",
@@ -660,20 +659,20 @@ describe "class including Harpy::Resource" do
660
659
  end
661
660
  end
662
661
  it "is true but doesn't touch attributes on 204" do
663
- response = Typhoeus::Response.new :code => 204
662
+ response = httpx_response :code => 204
664
663
  expect(Harpy.client).to receive(:put).with(url, :body => body).and_return response
665
664
  expect(subject.save).to be_truthy
666
665
  expect(subject.callbacks).to match_array([:save, :update])
667
666
  expect(subject.company_name).to eq("Stark Enterprises")
668
667
  end
669
668
  it "raises Harpy::Unauthorized on 401" do
670
- response = Typhoeus::Response.new :code => 401
669
+ response = httpx_response :code => 401
671
670
  expect(Harpy.client).to receive(:put).with(url, :body => body).and_return response
672
671
  expect { subject.save }.to raise_error Harpy::Unauthorized, "Server returned a 401 response code"
673
672
  expect(subject.callbacks).to match_array([:save, :update])
674
673
  end
675
674
  it "is false and fills in errors on 422" do
676
- response = Typhoeus::Response.new :code => 422, :body => <<-eos
675
+ response = httpx_response :code => 422, :body => <<-eos
677
676
  {
678
677
  "firstname": "Anthony",
679
678
  "errors": {
@@ -690,7 +689,7 @@ describe "class including Harpy::Resource" do
690
689
  expect(subject.company_name).to eq("Stark Enterprises")
691
690
  end
692
691
  it "delegates other response codes to client" do
693
- response = Typhoeus::Response.new :code => 500
692
+ response = httpx_response :code => 500
694
693
  expect(Harpy.client).to receive(:put).with(url, :body => body).and_return response
695
694
  expect(Harpy.client).to receive(:invalid_code).with(response)
696
695
  subject.save
@@ -717,7 +716,7 @@ describe "class including Harpy::Resource" do
717
716
  end
718
717
  [200, 201, 302].each do |response_code|
719
718
  it "is true and merges response attributes on #{response_code}" do
720
- response = Typhoeus::Response.new :code => response_code, :body => <<-eos
719
+ response = httpx_response :code => response_code, :body => <<-eos
721
720
  {
722
721
  "firstname": "Anthony",
723
722
  "urn": "urn:harpy:user:1",
@@ -734,20 +733,20 @@ describe "class including Harpy::Resource" do
734
733
  end
735
734
  end
736
735
  it "is true but doesn't touch attributes on 204" do
737
- response = Typhoeus::Response.new :code => 204
736
+ response = httpx_response :code => 204
738
737
  expect(Harpy.client).to receive(:delete).with(url).and_return response
739
738
  expect(subject.destroy).to be_truthy
740
739
  expect(subject.callbacks).to match_array([:destroy])
741
740
  expect(subject.company_name).to eq("Stark Enterprises")
742
741
  end
743
742
  it "raises Harpy::Unauthorized on 401" do
744
- response = Typhoeus::Response.new :code => 401
743
+ response = httpx_response :code => 401
745
744
  expect(Harpy.client).to receive(:delete).with(url).and_return response
746
745
  expect { subject.destroy }.to raise_error Harpy::Unauthorized, "Server returned a 401 response code"
747
746
  expect(subject.callbacks).to match_array([:destroy])
748
747
  end
749
748
  it "is false and fills in errors on 422" do
750
- response = Typhoeus::Response.new :code => 422, :body => <<-eos
749
+ response = httpx_response :code => 422, :body => <<-eos
751
750
  {
752
751
  "firstname": "Anthony",
753
752
  "errors": {
@@ -764,7 +763,7 @@ describe "class including Harpy::Resource" do
764
763
  expect(subject.company_name).to eq("Stark Enterprises")
765
764
  end
766
765
  it "delegates other response codes to client" do
767
- response = Typhoeus::Response.new :code => 500
766
+ response = httpx_response :code => 500
768
767
  expect(Harpy.client).to receive(:delete).with(url).and_return response
769
768
  expect(Harpy.client).to receive(:invalid_code).with(response)
770
769
  subject.destroy
data/spec/spec_helper.rb CHANGED
@@ -1,10 +1,39 @@
1
+ require "webmock/rspec"
2
+ require "httpx"
1
3
  require "harpy"
2
- require "typhoeus"
4
+
5
+ WebMock.disable_net_connect!
3
6
 
4
7
  Dir[File.expand_path("../support/**/*.rb", __FILE__)].each{|f| require f}
5
8
 
9
+ module HTTPXSpecHelpers
10
+ def httpx_response(options)
11
+ session = HTTPX::Session.new
12
+ request = session.build_request("GET", "http://localhost")
13
+ headers = options[:headers] || {}
14
+ if headers.is_a?(String)
15
+ headers = headers.lines.each_with_object({}) do |line, parsed|
16
+ name, value = line.split(":", 2)
17
+ parsed[name.strip] = value.strip if value
18
+ end
19
+ end
20
+
21
+ response = HTTPX::Response.new(request, options[:code], "1.1", headers)
22
+ response << options[:body] if options[:body]
23
+ response.finish!
24
+ response
25
+ end
26
+
27
+ def httpx_error_response(error)
28
+ session = HTTPX::Session.new
29
+ request = session.build_request("GET", "http://localhost")
30
+ HTTPX::ErrorResponse.new(request, error)
31
+ end
32
+ end
33
+
6
34
  RSpec.configure do |config|
35
+ config.include HTTPXSpecHelpers
7
36
  config.after(:each) do
8
- Typhoeus::Expectation.clear
37
+ WebMock.reset!
9
38
  end
10
39
  end
metadata CHANGED
@@ -1,30 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: harpy
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.0.0.preview1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joseph HALTER
8
8
  - Jonathan TRON
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2019-04-27 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: typhoeus
14
+ name: httpx
16
15
  requirement: !ruby/object:Gem::Requirement
17
16
  requirements:
18
17
  - - ">="
19
18
  - !ruby/object:Gem::Version
20
- version: 0.6.5
19
+ version: '1.0'
21
20
  type: :runtime
22
21
  prerelease: false
23
22
  version_requirements: !ruby/object:Gem::Requirement
24
23
  requirements:
25
24
  - - ">="
26
25
  - !ruby/object:Gem::Version
27
- version: 0.6.5
26
+ version: '1.0'
28
27
  - !ruby/object:Gem::Dependency
29
28
  name: activesupport
30
29
  requirement: !ruby/object:Gem::Requirement
@@ -95,6 +94,20 @@ dependencies:
95
94
  - - ">="
96
95
  - !ruby/object:Gem::Version
97
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: webmock
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
98
111
  description: Client for REST API with HATEOAS
99
112
  email:
100
113
  - joseph.halter@thetalentbox.com
@@ -103,9 +116,10 @@ executables: []
103
116
  extensions: []
104
117
  extra_rdoc_files: []
105
118
  files:
119
+ - ".forgejo/workflows/test.yml"
120
+ - ".github/workflows/test.yml"
106
121
  - ".gitignore"
107
122
  - ".rspec"
108
- - ".travis.yml"
109
123
  - Gemfile
110
124
  - LICENSE
111
125
  - README.md
@@ -127,7 +141,6 @@ homepage: https://github.com/TalentBox/harpy
127
141
  licenses:
128
142
  - MIT
129
143
  metadata: {}
130
- post_install_message:
131
144
  rdoc_options: []
132
145
  require_paths:
133
146
  - lib
@@ -135,15 +148,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
135
148
  requirements:
136
149
  - - ">="
137
150
  - !ruby/object:Gem::Version
138
- version: '0'
151
+ version: '3.4'
139
152
  required_rubygems_version: !ruby/object:Gem::Requirement
140
153
  requirements:
141
154
  - - ">="
142
155
  - !ruby/object:Gem::Version
143
156
  version: '0'
144
157
  requirements: []
145
- rubygems_version: 3.0.3
146
- signing_key:
158
+ rubygems_version: 4.0.23
147
159
  specification_version: 4
148
160
  summary: Client for REST API
149
161
  test_files:
data/.travis.yml DELETED
@@ -1,7 +0,0 @@
1
- rvm:
2
- - 2.4.6
3
- - 2.5.5
4
- - 2.6.3
5
- - jruby-9.2.7.0
6
- notifications:
7
- disabled: true