pebblebed 0.3.20 → 0.3.21

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
  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