twirp 1.7.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: d0046cb23f769232f34b2ec9b85961ee1311e59d68458b08539a0bb4f5789e83
4
- data.tar.gz: dae8b79d9d9b05f95916f7676a228732f1ca3f3779174fde51a2a2f0e7b09f87
3
+ metadata.gz: 3f17d89c29073da7ebffa780c9e8577e10f2537e28900344d8a055e098aa5f3d
4
+ data.tar.gz: 2932fb1abec1db6b14c3b39e8b7d2f81e318c0bdc02639d83a99bdfacd26001f
5
5
  SHA512:
6
- metadata.gz: db7e199e1a1593e4f6493fa8675018740d8d946f418ef02964c64d6a94b3410aa7ec1e79f5655fea89741cf65acbd0bce7003bdc844d9640d577b9a81ac27c24
7
- data.tar.gz: db7a5ce35a8ca7b0b532594b0535c350f96667c4530830be14dfdb4016f142c21ff3939375183f7836d752ba3c31af70a910785829820b0ff7d1127f0b683c9a
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, ignore_unknown_fields: true)
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, emit_defaults: true)
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
@@ -12,5 +12,5 @@
12
12
  # permissions and limitations under the License.
13
13
 
14
14
  module Twirp
15
- VERSION = "1.7.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,15 @@ 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
+
67
76
  def test_successful_json_request_emit_defaults
68
77
  rack_env = json_req "/example.Haberdasher/MakeHat", inches: 0 # default int value
69
78
  status, headers, body = haberdasher_service.call(rack_env)
@@ -188,6 +197,20 @@ class ServiceTest < Minitest::Test
188
197
  assert_equal({"inches" => 10, "color" => "white"}, JSON.parse(body[0]))
189
198
  end
190
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
+
191
214
  def test_bad_route_triggers_on_error_hooks
192
215
  svc = haberdasher_service
193
216
 
@@ -809,6 +832,12 @@ class ServiceTest < Minitest::Test
809
832
  "CONTENT_TYPE" => "application/json"
810
833
  end
811
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
+
812
841
  def proto_req(path, proto_message)
813
842
  Rack::MockRequest.env_for path, method: "POST",
814
843
  input: proto_message.class.encode(proto_message),
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.7.0
4
+ version: 1.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyrus A. Forbes
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-11-05 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