webmock-twirp 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 165be6973cc98a0dcfb538746766610afa45c02a344bf3c79f0567c36ab92d4c
4
+ data.tar.gz: 65a3021683eff819b8a5e1ca6e54d8d4eed63ed9364fd2631cb5308772243ade
5
+ SHA512:
6
+ metadata.gz: e5c02e2bfbe249a15af9aa36b1701e894550a330a72da318bec451a142e2d053ce40bc7c112e43057f1f216c8a0429dc3a670b8c112d136cd167ab482dbc9585
7
+ data.tar.gz: 6e1839bcd0cf51cb664cdda48408e8bcb89aec4dfbe60ba142cdd33f19b3462257ed56b5096fd9f338996257ec76b5121a2a8028f9e2ae848d2b1e79efd2d433
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ### v0.0.1 (2022-10-07)
2
+ - stub_twirp_request
3
+
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Daniel Pepper
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.
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ WebMock::Twirp
2
+ ======
3
+ Twirp support for [WebMock](https://github.com/bblimke/webmock). All our favorite http request stubbing for Twirp RPCs - message and error serialization done automatically.
4
+
5
+
6
+ ```ruby
7
+ require "webmock-twirp"
8
+
9
+ stub_twirp_request(MyTwirpClient, :optional_rpc_method)
10
+
11
+ stub_twirp_request(...).with(my_request_message: /^foo/)
12
+
13
+ # or use block mode
14
+ stub_twirp_request(...).with do |request|
15
+ request # the Twirp request, aka. proto message, used to initiate the request
16
+ request.my_request_message == "hello"
17
+ end
18
+
19
+
20
+ stub_twirp_request(...).and_return(return_message: "yo yo")
21
+ stub_twirp_request(...).and_return(404) # results in a Twirp::Error.not_found
22
+
23
+ # or use block mode
24
+ stub_twirp_request(...).and_return do |request|
25
+ { response_message: "oh hi" } # will get properly packaged up automagically
26
+ end
27
+ ```
28
+
29
+
30
+ ----
31
+ ## Contributing
32
+
33
+ Yes please :)
34
+
35
+ 1. Fork it
36
+ 1. Create your feature branch (`git checkout -b my-feature`)
37
+ 1. Ensure the tests pass (`bundle exec rspec`)
38
+ 1. Commit your changes (`git commit -am 'awesome new feature'`)
39
+ 1. Push your branch (`git push origin my-feature`)
40
+ 1. Create a Pull Request
41
+
42
+
43
+ ----
44
+ ![Gem](https://img.shields.io/gem/dt/webmock-twirp?style=plastic)
45
+ [![codecov](https://codecov.io/gh/dpep/webmock-twirp/branch/main/graph/badge.svg)](https://codecov.io/gh/dpep/webmock-twirp)
@@ -0,0 +1,149 @@
1
+ module WebMock
2
+ module Twirp
3
+ class RequestStub < WebMock::RequestStub
4
+ def initialize(client_or_service, rpc_name = nil)
5
+ klass = client_or_service.is_a?(Class) ? client_or_service : client_or_service.class
6
+
7
+ unless klass < ::Twirp::Client || klass < ::Twirp::Service
8
+ raise TypeError, "expected Twirp Client or Service, found: #{client_or_service}"
9
+ end
10
+
11
+ @rpcs = klass.rpcs
12
+ uri = "/#{klass.service_full_name}"
13
+
14
+ if rpc_name
15
+ rpc_info = rpcs.values.find do |x|
16
+ x[:rpc_method] == rpc_name.to_sym || x[:ruby_method] == rpc_name.to_sym
17
+ end
18
+
19
+ raise ArgumentError, "invalid rpc method: #{rpc_name}" unless rpc_info
20
+
21
+ uri += "/#{rpc_info[:rpc_method]}"
22
+ else
23
+ uri += "/[^/]+"
24
+ end
25
+
26
+ super(:post, /#{uri}$/)
27
+ end
28
+
29
+ def with(request = nil, **attrs, &block)
30
+ unless request.nil? || attrs.empty?
31
+ raise ArgumentError, "specify request or attrs, but not both"
32
+ end
33
+
34
+ request_matcher = if request
35
+ unless request.is_a?(Google::Protobuf::MessageExts)
36
+ raise TypeError, "Expected request to be Protobuf::MessageExts, found: #{request}"
37
+ end
38
+
39
+ { body: request.to_proto }
40
+ end
41
+
42
+ decoder = ->(request) do
43
+ input_class = rpc_from_request(request)[:input_class]
44
+
45
+ matched = true
46
+ decoded_request = input_class.decode(request.body)
47
+
48
+ if attrs.any?
49
+ attr_matcher = Matchers::HashIncludingMatcher.new(**attrs)
50
+ attr_hash = WebMock::Util::HashKeysStringifier.stringify_keys!(decoded_request.to_h, deep: true)
51
+
52
+ matched &= attr_matcher === attr_hash
53
+ end
54
+
55
+ if block
56
+ matched &= block.call(decoded_request)
57
+ end
58
+
59
+ matched
60
+ end if attrs.any? || block_given?
61
+
62
+ super(request_matcher || {}, &decoder)
63
+ end
64
+
65
+ def to_return(*responses, &block)
66
+ unless responses.empty? || block.nil?
67
+ raise ArgumentError, "specify responses or block, but not both"
68
+ end
69
+
70
+ # if no args, provide default response
71
+ responses << nil if responses.empty? && block.nil?
72
+
73
+ response_hashes = responses.map do |response|
74
+ ->(request) do
75
+ # determine msg type and package response
76
+ output_class = rpc_from_request(request)[:output_class]
77
+ generate_http_response(output_class, response)
78
+ end
79
+ end
80
+
81
+ decoder = ->(request) do
82
+ # determine msg type and call provided block
83
+ rpc = rpc_from_request(request)
84
+ res = block.call(rpc[:input_class].decode(request.body))
85
+ generate_http_response(rpc[:output_class], res)
86
+ end if block_given?
87
+
88
+ super(*response_hashes, &decoder)
89
+ end
90
+
91
+ def to_return_json(*)
92
+ raise NotImplementedError
93
+ end
94
+
95
+ private
96
+
97
+ attr_reader :rpcs
98
+
99
+ def rpc_from_request(request_signature)
100
+ rpcs[request_signature.uri.path.split("/").last]
101
+ end
102
+
103
+ def generate_http_response(msg_class, obj)
104
+ res = case obj
105
+ when nil
106
+ msg_class.new
107
+ when Hash
108
+ msg_class.new(**obj)
109
+ when Google::Protobuf::MessageExts
110
+ unless obj.is_a?(msg_class)
111
+ raise TypeError, "Expected type #{msg_class}, found #{obj}"
112
+ end
113
+
114
+ obj
115
+ when ::Twirp::Error
116
+ obj
117
+ when Symbol
118
+ if ::Twirp::Error.valid_code?(obj)
119
+ ::Twirp::Error.new(obj, obj)
120
+ else
121
+ raise ArgumentError, "invalid error code: #{obj}"
122
+ end
123
+ when Integer
124
+ if code = ::Twirp::ERROR_CODES_TO_HTTP_STATUS.key(obj)
125
+ ::Twirp::Error.new(code, code)
126
+ else
127
+ raise ArgumentError, "invalid error code: #{obj}"
128
+ end
129
+ else
130
+ raise NotImplementedError
131
+ end
132
+
133
+ if res.is_a?(Google::Protobuf::MessageExts)
134
+ {
135
+ status: 200,
136
+ headers: { "Content-Type" => ::Twirp::Encoding::PROTO },
137
+ body: res.to_proto,
138
+ }
139
+ else # Twirp::Error
140
+ {
141
+ status: ::Twirp::ERROR_CODES_TO_HTTP_STATUS[res.code],
142
+ headers: { "Content-Type" => ::Twirp::Encoding::JSON },
143
+ body: ::Twirp::Encoding.encode_json(res.to_h),
144
+ }
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,5 @@
1
+ module WebMock
2
+ module Twirp
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,40 @@
1
+ require "google/protobuf"
2
+ require "twirp"
3
+ require "webmock"
4
+ require "webmock/twirp/request_stub"
5
+ require "webmock/twirp/version"
6
+
7
+
8
+ module WebMock
9
+ module Twirp
10
+ extend self
11
+
12
+ private
13
+
14
+ module API
15
+ def stub_twirp_request(...)
16
+ WebMock::StubRegistry.instance.register_request_stub(
17
+ WebMock::Twirp::RequestStub.new(...),
18
+ )
19
+ end
20
+
21
+ # def a_twirp_request(uri)
22
+ # WebMock::RequestPattern.new(:post, uri)
23
+ # end
24
+ end
25
+ end
26
+ end
27
+
28
+
29
+ if $LOADED_FEATURES.find { |x| x =~ %r{/webmock/rspec.rb} }
30
+ # require "webmock/rspec"
31
+ RSpec.configure { |c| c.include WebMock::Twirp::API }
32
+ else
33
+ # patch Webmock to also export stub_twirp_request
34
+
35
+ module WebMock
36
+ module API
37
+ include WebMock::Twirp::API
38
+ end
39
+ end
40
+ end
@@ -0,0 +1 @@
1
+ require "webmock/twirp"
@@ -0,0 +1,31 @@
1
+ package_name = File.basename(__FILE__).split(".")[0]
2
+ load Dir.glob("lib/**/version.rb")[0]
3
+
4
+ package = WebMock::Twirp
5
+
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = package_name
9
+ s.version = package.const_get "VERSION"
10
+ s.authors = ["Daniel Pepper"]
11
+ s.summary = package.to_s
12
+ s.description = "Twirp support for WebMock"
13
+ s.homepage = "https://github.com/dpep/#{package_name}"
14
+ s.license = "MIT"
15
+
16
+ s.files = Dir[
17
+ __FILE__,
18
+ 'lib/**/*',
19
+ 'CHANGELOG*',
20
+ 'LICENSE*',
21
+ 'README*',
22
+ ]
23
+
24
+ s.add_dependency "webmock", ">= 3"
25
+ s.add_dependency "twirp", ">= 1"
26
+
27
+ s.add_development_dependency "byebug"
28
+ s.add_development_dependency "codecov"
29
+ s.add_development_dependency "rspec"
30
+ s.add_development_dependency "simplecov"
31
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: webmock-twirp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Pepper
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-10-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: webmock
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: twirp
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: codecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Twirp support for WebMock
98
+ email:
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - CHANGELOG.md
104
+ - LICENSE.txt
105
+ - README.md
106
+ - lib/webmock-twirp.rb
107
+ - lib/webmock/twirp.rb
108
+ - lib/webmock/twirp/request_stub.rb
109
+ - lib/webmock/twirp/version.rb
110
+ - webmock-twirp.gemspec
111
+ homepage: https://github.com/dpep/webmock-twirp
112
+ licenses:
113
+ - MIT
114
+ metadata: {}
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubygems_version: 3.3.7
131
+ signing_key:
132
+ specification_version: 4
133
+ summary: Gem::Specification::WebMock::Twirp
134
+ test_files: []