opener-webservice 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d3c9e68800786848df8f2c1310b2063edbf58939
4
+ data.tar.gz: e3b445eb8a225c5811663fc7dc5b0077c9e16077
5
+ SHA512:
6
+ metadata.gz: 2f8b4978233d0f11a62828388c425e535be83c4dd8de2a78b06d1fec9b74c322a752dcf0ad5a65a3f365cf318837e0b7fa49d95eac38c10dcf1672c4658e946b
7
+ data.tar.gz: 7980a3858ddfaf44d0c24c89097ee025352dbea0108b06085634f7df316d04e90c0fb5128705126919d8fb53e8cdff8fcb54b2abc93d7d12eb6b491ed2c951ee
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Opener::Webservice
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'opener-webservice'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install opener-webservice
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,7 @@
1
+ require 'sinatra/base'
2
+
3
+ module Opener
4
+ class Webservice < Sinatra::Base
5
+ VERSION = "1.0.0"
6
+ end
7
+ end
@@ -0,0 +1,274 @@
1
+ require "uuidtools"
2
+ require "sinatra/base"
3
+ require "json"
4
+ require "opener/webservice/version"
5
+
6
+ module Opener
7
+ class Webservice < Sinatra::Base
8
+ configure do
9
+ enable :logging
10
+ end
11
+
12
+ configure :development do
13
+ set :raise_errors, true
14
+ set :dump_errors, true
15
+ end
16
+
17
+ ##
18
+ # Presents a simple form that can be used for getting the NER of a KAF
19
+ # document.
20
+ #
21
+ get '/' do
22
+ erb :index
23
+ end
24
+
25
+ ##
26
+ # Puts the text through the primary processor
27
+ #
28
+ # @param [Hash] params The POST parameters.
29
+ #
30
+ # @option params [String] :input the input to send to the processor
31
+ # @option params [Array<String>] :callbacks A collection of callback URLs
32
+ # that act as a chain. The results are posted to the first URL which is
33
+ # then shifted of the list.
34
+ # @option params [String] :error_callback Callback URL to send errors to
35
+ # when using the asynchronous setup.
36
+ #
37
+ post '/' do
38
+ if !params[:input] or params[:input].strip.empty?
39
+ logger.error('Failed to process the request: no input specified')
40
+
41
+ halt(400, 'No input specified')
42
+ end
43
+
44
+ callbacks = extract_callbacks(params[:callbacks])
45
+ error_callback = params[:error_callback]
46
+
47
+ if callbacks.empty?
48
+ process_sync
49
+ else
50
+ process_async(callbacks, error_callback)
51
+ end
52
+ end
53
+
54
+ ##
55
+ # @return [HTTPClient]
56
+ #
57
+ def self.http_client
58
+ return @http_client || new_http_client
59
+ end
60
+
61
+ ##
62
+ # @return [HTTPClient]
63
+ #
64
+ def self.new_http_client
65
+ client = HTTPClient.new
66
+ client.connect_timeout = 120
67
+
68
+ return client
69
+ end
70
+
71
+ ##
72
+ # Specifies the text processor to use or returns it if no parameter is
73
+ # given.
74
+ #
75
+ # @param [Class] processor
76
+ # @return [Class]
77
+ #
78
+ def self.text_processor(processor=nil)
79
+ if processor.nil?
80
+ return @processor
81
+ else
82
+ @processor = processor
83
+ end
84
+ end
85
+
86
+ ##
87
+ # Specifies what parameters are accepted.
88
+ #
89
+ # @param [Array] array The parameters to accept.
90
+ #
91
+ def self.accepted_params(*array)
92
+ if array.empty?
93
+ return @accepted_params
94
+ else
95
+ @accepted_params = array
96
+ end
97
+ end
98
+
99
+ ##
100
+ # @return [Class]
101
+ #
102
+ def text_processor
103
+ self.class.text_processor
104
+ end
105
+
106
+ ##
107
+ # @return [Array]
108
+ #
109
+ def accepted_params
110
+ self.class.accepted_params
111
+ end
112
+
113
+ ##
114
+ # Processes the request synchronously.
115
+ #
116
+ def process_sync
117
+ output, type = analyze(filtered_params)
118
+ content_type(type)
119
+ body(output)
120
+ end
121
+
122
+ ##
123
+ # Filter the params hash based on the accepted_params
124
+ #
125
+ # @return [Hash] accepted params
126
+ #
127
+ def filtered_params
128
+ options = params.select{|k,v| accepted_params.include?(k.to_sym)}
129
+ cleaned = {}
130
+ options.each_pair do |k, v|
131
+ v = true if v == "true"
132
+ v = false if v == "false"
133
+ cleaned[k.to_sym] = v
134
+ end
135
+
136
+ return cleaned
137
+ end
138
+
139
+ ##
140
+ # Processes the request asynchronously.
141
+ #
142
+ # @param [Array] callbacks The callback URLs to use.
143
+ #
144
+ def process_async(callbacks, error_callback)
145
+ request_id = get_request_id
146
+ output_url = callbacks.last
147
+ Thread.new do
148
+ analyze_async(filtered_params, request_id, callbacks, error_callback)
149
+ end
150
+
151
+ content_type :json
152
+
153
+ {
154
+ :request_id => request_id.to_s,
155
+ :output_url => [output_url, request_id].join("/")
156
+ }.to_json
157
+ end
158
+
159
+ ##
160
+ # Gets the Analyzed output of an input.
161
+ #
162
+ # @param [Hash] options The options for the text_processor
163
+ # @return [String] output the output of the text_processor
164
+ # @return [Symbol] type the output type ot the text_processor
165
+ #
166
+ # @raise RunetimeError Raised when the tagging process failed.
167
+ #
168
+ def analyze(options)
169
+ processor = text_processor.new(options)
170
+ output, error, status = processor.run(options[:input])
171
+
172
+ if processor.respond_to?(:output_type)
173
+ type = processor.output_type
174
+ else
175
+ type = :xml
176
+ end
177
+
178
+ raise(error) if !status.nil? && !status.success?
179
+
180
+ return output, type
181
+ end
182
+
183
+ ##
184
+ # Gets the NER of a KAF document and submits it to a callback URL.
185
+ #
186
+ # @param [String] text
187
+ # @param [String] request_id
188
+ # @param [Array] callbacks
189
+ # @param [String] error_callback
190
+ #
191
+ def analyze_async(options, request_id, callbacks, error_callback = nil)
192
+ begin
193
+ output, _ = analyze(options)
194
+ rescue => error
195
+ logger.error("Failed to process input: #{error.inspect}")
196
+
197
+ submit_error(error_callback, error.message) if error_callback
198
+ end
199
+
200
+ url = callbacks.shift
201
+
202
+ logger.info("Submitting results to #{url}")
203
+
204
+ begin
205
+ process_callback(url, output, request_id, callbacks, error_callback)
206
+ rescue => error
207
+ logger.error("Failed to submit the results: #{error.inspect}")
208
+
209
+ submit_error(error_callback, error.message) if error_callback
210
+ end
211
+ end
212
+
213
+ ##
214
+ # @param [String] url
215
+ # @param [String] text
216
+ # @param [String] request_id
217
+ # @param [Array] callbacks
218
+ #
219
+ def process_callback(url, text, request_id, callbacks, error_callback)
220
+ # FIXME: this is a bit of a hack to prevent the webservice from clogging
221
+ # Airbrake during the hackathon. For whatever reason somebody is posting
222
+ # internal server errors from *somewhere*. Validation? What's that?
223
+ return if text =~ /^internal server error/i
224
+
225
+ output = {
226
+ :input => text,
227
+ :request_id => request_id,
228
+ :'callbacks[]' => callbacks,
229
+ :error_callback => error_callback
230
+ }
231
+
232
+ http_client.post_async(
233
+ url,
234
+ :body => filtered_params.merge(output)
235
+ )
236
+ end
237
+
238
+ ##
239
+ # @param [String] url
240
+ # @param [String] message
241
+ #
242
+ def submit_error(url, message)
243
+ http_client.post_async(url, :body => {:error => message})
244
+ end
245
+
246
+ ##
247
+ # Returns an Array containing the callback URLs, ignoring empty values.
248
+ #
249
+ # @param [Array|String] input
250
+ # @return [Array]
251
+ #
252
+ def extract_callbacks(input)
253
+ return [] if input.nil? || input.empty?
254
+
255
+ callbacks = input.compact.reject(&:empty?)
256
+
257
+ return callbacks
258
+ end
259
+
260
+ ##
261
+ # @return [String]
262
+ #
263
+ def get_request_id
264
+ return params[:request_id] || UUIDTools::UUID.random_create
265
+ end
266
+
267
+ ##
268
+ # @see Opener::Webservice.http_client
269
+ #
270
+ def http_client
271
+ return self.class.http_client
272
+ end
273
+ end
274
+ end
@@ -0,0 +1,23 @@
1
+ require File.expand_path('../lib/opener/webservice/version', __FILE__)
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "opener-webservice"
5
+ spec.version = Opener::Webservice::VERSION
6
+ spec.authors = ["development@olery.com"]
7
+ spec.summary = %q{Basic webservice hooks for the opener toolchain}
8
+ spec.description = spec.summary
9
+
10
+ spec.files = Dir.glob([
11
+ 'lib/**/*',
12
+ '*.gemspec',
13
+ 'README.md'
14
+ ]).select { |file| File.file?(file) }
15
+
16
+ spec.add_development_dependency "bundler", "~> 1.3"
17
+ spec.add_development_dependency "rake"
18
+ spec.add_development_dependency "pry"
19
+
20
+ spec.add_dependency "sinatra", "~> 1.4.3"
21
+ spec.add_dependency "uuidtools"
22
+ spec.add_dependency "json"
23
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opener-webservice
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - development@olery.com
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
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: sinatra
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.4.3
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.4.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: uuidtools
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
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: json
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Basic webservice hooks for the opener toolchain
98
+ email:
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - README.md
104
+ - lib/opener/webservice.rb
105
+ - lib/opener/webservice/version.rb
106
+ - opener-webservice.gemspec
107
+ homepage:
108
+ licenses: []
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.2.2
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Basic webservice hooks for the opener toolchain
130
+ test_files: []
131
+ has_rdoc: