pebblebed 0.3.20 → 0.3.21

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8310ad73ddbc179d054ac75fdf33890d51e3fa61
4
- data.tar.gz: d60bca28ff3cce1676d00655f04a9717bcd65092
3
+ metadata.gz: 65f960100fa8f06519e6d5322a3e94a3387beb27
4
+ data.tar.gz: c6135f4cf37b105aa16ef063f707ce8661b7c439
5
5
  SHA512:
6
- metadata.gz: 2edd27c829d0567756bb27ff2d9e66401db37baad0c9ee35a5334ebcd8c2d740759137ea6e41812fbe5f8cef095d196eaeec39db071cbdf8b19a16309692c1d8
7
- data.tar.gz: e75bbaef43db033a865b5e9965b818994bae9bf01ce379c8102143b83d74a13458a5645d84a05f494d93fa0f95e4b9cd50f3d7a8f1d352bb597d02c0c6df098d
6
+ metadata.gz: a5a203b1048b041abb697811f1084a7384ea30ed101f601d17fddaa3978f871b65afa53309de299e94eb7dfccceb818a43dd28cb27e158b6374acc4fb32fae60
7
+ data.tar.gz: e15a17c6a78643194245573635a0155adffd8af97677483f7cb71b642066afacd837fcc9e5ec423ae3c0aa3952c356a596dcaf308fe22f32a2123a8a3c4a783e
@@ -1,4 +1,5 @@
1
1
  require 'deepstruct'
2
+ require_relative '../ndjson'
2
3
 
3
4
  module Pebblebed
4
5
  class GenericClient < AbstractClient
@@ -22,10 +23,20 @@ module Pebblebed
22
23
  end
23
24
  end
24
25
 
25
- def stream(method, url = '', params = {}, options = {})
26
- on_data = options[:on_data] or raise "Option :on_data must be specified"
26
+ def stream(method, url = '', params = {}, on_data:, accept: nil)
27
27
  method_name = "stream_#{method.to_s.downcase}"
28
28
  raise "Method not supported for streaming" unless Pebblebed::Http.respond_to?(method_name)
29
+
30
+ if accept == 'application/x-ndjson'
31
+ buffer = NdjsonBuffer.new(on_data)
32
+ response = Pebblebed::Http.send(method_name, service_url(url), service_params(params),
33
+ on_data: ->(data) {
34
+ buffer << data
35
+ })
36
+ buffer.check_end!
37
+ return response
38
+ end
39
+
29
40
  return Pebblebed::Http.send(method_name, service_url(url), service_params(params),
30
41
  on_data: on_data)
31
42
  end
@@ -0,0 +1,51 @@
1
+ module Pebblebed
2
+
3
+ # Helper class which buffers an NDJSON input stream, parsing each
4
+ # complete line into a handler as JSON.
5
+ class NdjsonBuffer
6
+
7
+ # Initializes with handler. The handler must provide a method 'call'
8
+ # which will be called with each JSON payload.
9
+ def initialize(handler)
10
+ @handler = handler
11
+ @buf = ''
12
+ @end = false
13
+ end
14
+
15
+ # Returns true if the end of the stream has been reached.
16
+ def ended?
17
+ @end
18
+ end
19
+
20
+ # Checks whether this buffer has reached its end. Raises IOError
21
+ # otherwise.
22
+ def check_end!
23
+ unless @end
24
+ # We need to raise this to signal that a complete contents
25
+ # was not returned.
26
+ raise IOError, "End of stream expected"
27
+ end
28
+ end
29
+
30
+ # Feeds data into the buffer.
31
+ def <<(data)
32
+ return if data.empty?
33
+ @buf << data
34
+ begin
35
+ if /\A(?<line>[^\n]*)\n(?<rest>.*)\z/m =~ @buf
36
+ if line.length == 0 && rest.length == 0
37
+ @buf.clear
38
+ @end = true
39
+ else
40
+ payload, @buf = JSON.parse(line), rest
41
+ @handler.call(payload)
42
+ end
43
+ else
44
+ break
45
+ end
46
+ end until @end
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -1,3 +1,3 @@
1
1
  module Pebblebed
2
- VERSION = "0.3.20"
2
+ VERSION = "0.3.21"
3
3
  end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
  require 'pebblebed/config'
3
3
  require 'pebblebed/clients/abstract_client'
4
4
  require 'pebblebed/clients/generic_client'
5
+ require 'pebblebed/http'
5
6
 
6
7
  module Pebblebed
7
8
  module Http
@@ -72,4 +73,118 @@ describe Pebblebed::GenericClient do
72
73
  end
73
74
  end
74
75
 
76
+ context 'NDJSON streams' do
77
+ context 'GET' do
78
+ it "streams response body" do
79
+ curl_result = DeepStruct.wrap({status: 200, body: nil})
80
+ allow(Pebblebed::Http).to receive(:stream_get).with(
81
+ URI.parse("http://example.org/"),
82
+ {"session" => "session_key"},
83
+ anything) { |_, _, options|
84
+ options[:on_data].call(%{{"hello":"world"}\n{"a":42}\n\n})
85
+ }.and_return(curl_result)
86
+
87
+ payloads = []
88
+ client = Pebblebed::GenericClient.new("session_key", "http://example.org/")
89
+ result = client.stream(:get, '/', {},
90
+ accept: 'application/x-ndjson',
91
+ on_data: ->(payload) {
92
+ payloads << payload
93
+ })
94
+ expect(payloads).to eq [
95
+ {'hello' => 'world'},
96
+ {'a' => 42}
97
+ ]
98
+ expect(result.status).to eq 200
99
+ expect(result.body).to eq nil
100
+ end
101
+
102
+ [
103
+ %{{"hello":"world"}\n},
104
+ %{{"hello":"world"}\n{"hello":"world"}}
105
+ ].each do |scenario|
106
+ it "detects incomplete stream" do
107
+ curl_result = DeepStruct.wrap({status: 200, body: nil})
108
+ allow(Pebblebed::Http).to receive(:stream_get).with(
109
+ URI.parse("http://example.org/"),
110
+ {"session" => "session_key"},
111
+ anything) { |_, _, options|
112
+ options[:on_data].call(scenario)
113
+ }.and_return(curl_result)
114
+
115
+ payloads = []
116
+ client = Pebblebed::GenericClient.new("session_key", "http://example.org/")
117
+ expect {
118
+ client.stream(:get, '/', {},
119
+ accept: 'application/x-ndjson',
120
+ on_data: ->(payload) {
121
+ payloads << payload
122
+ })
123
+ }.to raise_error(IOError)
124
+ end
125
+ end
126
+
127
+ it "raises error on bad JSON" do
128
+ curl_result = DeepStruct.wrap({status: 200, body: nil})
129
+ allow(Pebblebed::Http).to receive(:stream_get).with(
130
+ URI.parse("http://example.org/"),
131
+ {"session" => "session_key"},
132
+ anything) { |_, _, options|
133
+ options[:on_data].call(%{fnord\n})
134
+ }.and_return(curl_result)
135
+
136
+ payloads = []
137
+ client = Pebblebed::GenericClient.new("session_key", "http://example.org/")
138
+ expect {
139
+ client.stream(:get, '/', {},
140
+ accept: 'application/x-ndjson',
141
+ on_data: ->(payload) {
142
+ payloads << payload
143
+ })
144
+ }.to raise_error(JSON::ParserError)
145
+ end
146
+
147
+ it "returns HTTP status" do
148
+ curl_result = DeepStruct.wrap({status: 201, body: 'halp'})
149
+ allow(Pebblebed::Http).to receive(:stream_get).with(
150
+ URI.parse("http://example.org/"),
151
+ {"session" => "session_key"},
152
+ anything) { |_, _, options|
153
+ options[:on_data].call(%{"a"\n\n})
154
+ }.and_return(curl_result)
155
+
156
+ payloads = []
157
+ client = Pebblebed::GenericClient.new("session_key", "http://example.org/")
158
+ response = client.stream(:get, '/', {},
159
+ accept: 'application/x-ndjson',
160
+ on_data: ->(payload) {
161
+ payloads << payload
162
+ })
163
+ expect(payloads).to eq ['a']
164
+ expect(response.status).to eq 201
165
+ expect(response.body).to eq "halp"
166
+ end
167
+
168
+ it "raises on HTTP error" do
169
+ curl_result = DeepStruct.wrap({status: 500, body: 'halp'})
170
+ allow(Pebblebed::Http).to receive(:stream_get).with(
171
+ URI.parse("http://example.org/"),
172
+ {"session" => "session_key"},
173
+ anything) { |_, _, options|
174
+ raise Pebblebed::HttpError.new("error", 500, curl_result)
175
+ }
176
+
177
+ payloads = []
178
+ client = Pebblebed::GenericClient.new("session_key", "http://example.org/")
179
+ expect {
180
+ client.stream(:get, '/', {},
181
+ accept: 'application/x-ndjson',
182
+ on_data: ->(payload) {
183
+ payloads << payload
184
+ })
185
+ }.to raise_error(Pebblebed::HttpError)
186
+ end
187
+ end
188
+ end
189
+
75
190
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pebblebed
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.20
4
+ version: 0.3.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katrina Owen
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-10-11 00:00:00.000000000 Z
12
+ date: 2017-10-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -257,6 +257,7 @@ files:
257
257
  - lib/pebblebed/connector.rb
258
258
  - lib/pebblebed/http.rb
259
259
  - lib/pebblebed/labels.rb
260
+ - lib/pebblebed/ndjson.rb
260
261
  - lib/pebblebed/parts.rb
261
262
  - lib/pebblebed/river.rb
262
263
  - lib/pebblebed/river/subscription.rb