twirp 1.5.0 → 1.7.2

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: '07397331bd18c688d70740c3ad5cec2401103a325bab3331dda34d3072223c89'
4
- data.tar.gz: 684074c539bb00ddb32e814601378b6d3e0f967b48e8c758832a56a74ceba0a2
3
+ metadata.gz: 3f17d89c29073da7ebffa780c9e8577e10f2537e28900344d8a055e098aa5f3d
4
+ data.tar.gz: 2932fb1abec1db6b14c3b39e8b7d2f81e318c0bdc02639d83a99bdfacd26001f
5
5
  SHA512:
6
- metadata.gz: '079722ee218b74194884e10383fc9ebb53febf02e215d7fa001c14c864e3c2eff9fddee8079c29e06396a766901efab7e696c14d9f9e0afd6df1034a10e5aa56'
7
- data.tar.gz: d91d578d6cbd5245b38d5b1838892db43bc53730cdfb00391a1cad266d3193522625d87c3b5e2bfb0fc807bb90e6d1cd90063c95a139c5096ea4ef425106e2f7
6
+ metadata.gz: aca9edc089a15abfe4426880e8db8a8e2cac3f47ff056703e686dc14a3fe879a952a2b96c2de8bb06301b7406262601102c29fe48af6f9ead8775e9e2c76e35b
7
+ data.tar.gz: 10fc31c496e2fa4020ff9a643e08aa9ba333b0baa2324d1a42444ca6916d672a449eeef7ab22e674de81cc7a3e906d82fc97736714dfab29a999bfb4dd191f75
@@ -24,6 +24,7 @@ module Twirp
24
24
 
25
25
  package = opts[:package].to_s
26
26
  service = opts[:service].to_s
27
+ @strict = opts.fetch( :strict, false )
27
28
  raise ArgumentError.new("Missing option :service") if service.empty?
28
29
  @service_full_name = package.empty? ? service : "#{package}.#{service}"
29
30
  end
@@ -33,7 +34,8 @@ module Twirp
33
34
  def rpc(rpc_method, attrs={}, req_opts=nil)
34
35
  body = Encoding.encode_json(attrs)
35
36
 
36
- resp = self.class.make_http_request(@conn, @service_full_name, rpc_method, Encoding::JSON, req_opts, body)
37
+ encoding = @strict ? Encoding::JSON_STRICT : Encoding::JSON
38
+ resp = self.class.make_http_request(@conn, @service_full_name, rpc_method, encoding, req_opts, body)
37
39
  if resp.status != 200
38
40
  return ClientResp.new(nil, self.class.error_from_response(resp))
39
41
  end
@@ -17,13 +17,18 @@ module Twirp
17
17
 
18
18
  module Encoding
19
19
  JSON = "application/json"
20
+ # An opt-in content type useful when curling or manually testing a twirp
21
+ # service. This will fail if unknown fields are encountered. The return
22
+ # content type will be application/json.
23
+ JSON_STRICT = "application/json; strict=true"
20
24
  PROTO = "application/protobuf"
21
25
 
22
26
  class << self
23
27
 
24
28
  def decode(bytes, msg_class, content_type)
25
29
  case content_type
26
- when JSON then msg_class.decode_json(bytes)
30
+ when JSON then msg_class.decode_json(bytes, ignore_unknown_fields: true)
31
+ when JSON_STRICT then msg_class.decode_json(bytes, ignore_unknown_fields: false)
27
32
  when PROTO then msg_class.decode(bytes)
28
33
  else raise ArgumentError.new("Invalid content_type")
29
34
  end
@@ -31,7 +36,7 @@ module Twirp
31
36
 
32
37
  def encode(msg_obj, msg_class, content_type)
33
38
  case content_type
34
- when JSON then msg_class.encode_json(msg_obj)
39
+ when JSON, JSON_STRICT then msg_class.encode_json(msg_obj, emit_defaults: true)
35
40
  when PROTO then msg_class.encode(msg_obj)
36
41
  else raise ArgumentError.new("Invalid content_type")
37
42
  end
@@ -46,7 +51,7 @@ module Twirp
46
51
  end
47
52
 
48
53
  def valid_content_type?(content_type)
49
- content_type == JSON || content_type == PROTO
54
+ content_type == JSON || content_type == PROTO || content_type == JSON_STRICT
50
55
  end
51
56
 
52
57
  def valid_content_types
@@ -25,7 +25,7 @@ module Twirp
25
25
  already_exists: 409, # Conflict
26
26
  permission_denied: 403, # Forbidden
27
27
  unauthenticated: 401, # Unauthorized
28
- resource_exhausted: 403, # Forbidden
28
+ resource_exhausted: 429, # Too Many Requests
29
29
  failed_precondition: 412, # Precondition Failed
30
30
  aborted: 409, # Conflict
31
31
  out_of_range: 400, # Bad Request
@@ -12,5 +12,5 @@
12
12
  # permissions and limitations under the License.
13
13
 
14
14
  module Twirp
15
- VERSION = "1.5.0"
15
+ VERSION = "1.7.2"
16
16
  end
@@ -29,6 +29,20 @@ class ClientJSONTest < Minitest::Test
29
29
  assert_equal 3, resp.data["blah_resp"]
30
30
  end
31
31
 
32
+ def test_client_json_strict_encoding
33
+ c = Twirp::ClientJSON.new(conn_stub("/my.pkg.Talking/Blah") {|req|
34
+ assert_equal "application/json; strict=true", req.request_headers['Content-Type']
35
+ assert_equal '{"blah1":1,"blah2":2}', req.body # body is json
36
+
37
+ [200, {}, '{"blah_resp": 3}']
38
+ }, package: "my.pkg", service: "Talking", strict: true)
39
+
40
+ resp = c.rpc :Blah, blah1: 1, blah2: 2
41
+ assert_nil resp.error
42
+ refute_nil resp.data
43
+ assert_equal 3, resp.data["blah_resp"]
44
+ end
45
+
32
46
  def test_client_json_error
33
47
  c = Twirp::ClientJSON.new(conn_stub("/Foo/Foomo") {|req|
34
48
  [400, {}, '{"code": "invalid_argument", "msg": "dont like empty"}']
@@ -64,6 +64,24 @@ class ServiceTest < Minitest::Test
64
64
  assert_equal({"inches" => 10, "color" => "white"}, JSON.parse(body[0]))
65
65
  end
66
66
 
67
+ def test_successful_json_strict_request_emit_defaults
68
+ rack_env = json_strict_req "/example.Haberdasher/MakeHat", inches: 0 # default int value
69
+ status, headers, body = haberdasher_service.call(rack_env)
70
+
71
+ assert_equal 200, status
72
+ assert_equal 'application/json; strict=true', headers['Content-Type']
73
+ assert_equal({"inches" => 0, "color" => "white"}, JSON.parse(body[0]))
74
+ end
75
+
76
+ def test_successful_json_request_emit_defaults
77
+ rack_env = json_req "/example.Haberdasher/MakeHat", inches: 0 # default int value
78
+ status, headers, body = haberdasher_service.call(rack_env)
79
+
80
+ assert_equal 200, status
81
+ assert_equal 'application/json', headers['Content-Type']
82
+ assert_equal({"inches" => 0, "color" => "white"}, JSON.parse(body[0]))
83
+ end
84
+
67
85
  def test_successful_proto_request
68
86
  rack_env = proto_req "/example.Haberdasher/MakeHat", Example::Size.new(inches: 10)
69
87
  status, headers, body = haberdasher_service.call(rack_env)
@@ -170,6 +188,29 @@ class ServiceTest < Minitest::Test
170
188
  }, JSON.parse(body[0]))
171
189
  end
172
190
 
191
+ def test_json_request_ignores_unknown_fields
192
+ rack_env = json_req "/example.Haberdasher/MakeHat", inches: 10, fake: 3
193
+ status, headers, body = haberdasher_service.call(rack_env)
194
+
195
+ assert_equal 200, status
196
+ assert_equal 'application/json', headers['Content-Type']
197
+ assert_equal({"inches" => 10, "color" => "white"}, JSON.parse(body[0]))
198
+ end
199
+
200
+ def test_json_strict_request_fails_unknown_fields
201
+ rack_env = json_strict_req "/example.Haberdasher/MakeHat", inches: 10, fake: 3
202
+ status, headers, body = haberdasher_service.call(rack_env)
203
+
204
+ assert_equal 400, status
205
+ assert_equal 'application/json', headers['Content-Type']
206
+ assert_equal({
207
+ "code" => 'malformed',
208
+ "msg" => 'Invalid request body for rpc method "MakeHat" with Content-Type=application/json; strict=true: ' +
209
+ "Error occurred during parsing: No such field: fake",
210
+ "meta" => {"twirp_invalid_route" => "POST /example.Haberdasher/MakeHat"},
211
+ }, JSON.parse(body[0]))
212
+ end
213
+
173
214
  def test_bad_route_triggers_on_error_hooks
174
215
  svc = haberdasher_service
175
216
 
@@ -791,6 +832,12 @@ class ServiceTest < Minitest::Test
791
832
  "CONTENT_TYPE" => "application/json"
792
833
  end
793
834
 
835
+ def json_strict_req(path, attrs)
836
+ Rack::MockRequest.env_for path, method: "POST",
837
+ input: JSON.generate(attrs),
838
+ "CONTENT_TYPE" => "application/json; strict=true"
839
+ end
840
+
794
841
  def proto_req(path, proto_message)
795
842
  Rack::MockRequest.env_for path, method: "POST",
796
843
  input: proto_message.class.encode(proto_message),
@@ -19,11 +19,11 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.required_ruby_version = '>= 1.9'
22
- spec.add_runtime_dependency 'google-protobuf', '~> 3.0', '>= 3.0.0'
22
+ spec.add_runtime_dependency 'google-protobuf', '~> 3.0', '>= 3.7.0'
23
23
  spec.add_runtime_dependency 'faraday', '< 2' # for clients
24
24
 
25
25
  spec.add_development_dependency 'bundler', '~> 1'
26
26
  spec.add_development_dependency 'minitest', '>= 5'
27
27
  spec.add_development_dependency 'rake'
28
- spec.add_development_dependency 'rack', '>= 2.0.8'
28
+ spec.add_development_dependency 'rack', '>= 2.2.3'
29
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twirp
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyrus A. Forbes
@@ -9,28 +9,28 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-04-14 00:00:00.000000000 Z
12
+ date: 2020-11-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: google-protobuf
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - ">="
19
- - !ruby/object:Gem::Version
20
- version: 3.0.0
21
18
  - - "~>"
22
19
  - !ruby/object:Gem::Version
23
20
  version: '3.0'
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 3.7.0
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
27
27
  requirements:
28
- - - ">="
29
- - !ruby/object:Gem::Version
30
- version: 3.0.0
31
28
  - - "~>"
32
29
  - !ruby/object:Gem::Version
33
30
  version: '3.0'
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.7.0
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: faraday
36
36
  requirement: !ruby/object:Gem::Requirement
@@ -93,14 +93,14 @@ dependencies:
93
93
  requirements:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: 2.0.8
96
+ version: 2.2.3
97
97
  type: :development
98
98
  prerelease: false
99
99
  version_requirements: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: 2.0.8
103
+ version: 2.2.3
104
104
  description: Twirp is a simple RPC framework with protobuf service definitions. The
105
105
  Twirp gem provides native support for Ruby.
106
106
  email: