Class: Opener::Webservice::Server
- Inherits:
-
Sinatra::Base
- Object
- Sinatra::Base
- Opener::Webservice::Server
- Defined in:
- lib/opener/webservice/server.rb
Overview
The meat of the webservices: the actual Sinatra application. Components should extend this class and configure it (e.g. to specify what component class to use).
Constant Summary collapse
- INPUT_FIELDS =
List of fields that can contain input to process.
%w{input input_url}
Class Method Summary collapse
-
.accepted_params ⇒ Array
Returns the accepted component parameters.
-
.accepted_params=(params) ⇒ Object
Sets the accepted component parameters.
-
.text_processor ⇒ Class
Returns the text processor to use.
-
.text_processor=(processor) ⇒ Object
Sets the text processor to use.
Instance Method Summary collapse
-
#/ ⇒ Object
Processes the input using a component.
-
#analyze(options) ⇒ Array
Analyzes the input and returns an Array containing the output and content type.
-
#analyze_async(options, request_id) ⇒ Object
Analyzes the input asynchronously.
-
#async ⇒ Object
Runs the block in a separate thread.
-
#authenticate! ⇒ Object
Authenticates the current request.
-
#json_input? ⇒ TrueClass|FalseClass
Returns ‘true` if the input data is in JSON, false otherwise.
-
#params_from_json ⇒ Hash
Returns a Hash containing the parameters from a JSON payload.
-
#process_async(options) ⇒ Hash
Processes a request asynchronously, results are submitted to the next callback URL.
-
#process_sync(options) ⇒ String
Processes a request synchronously, results are sent as the response upon completion.
-
#submit_output(output, request_id, options) ⇒ Object
Submits the output to the next callback URL.
Class Method Details
.accepted_params ⇒ Array
Returns the accepted component parameters.
31 32 33 |
# File 'lib/opener/webservice/server.rb', line 31 def self.accepted_params return @accepted_params ||= [] end |
.accepted_params=(params) ⇒ Object
Sets the accepted component parameters. Parameter names are always stored as symbols.
22 23 24 |
# File 'lib/opener/webservice/server.rb', line 22 def self.accepted_params=(params) @accepted_params = params.map(&:to_sym) end |
.text_processor ⇒ Class
Returns the text processor to use.
49 50 51 |
# File 'lib/opener/webservice/server.rb', line 49 def self.text_processor return @text_processor end |
.text_processor=(processor) ⇒ Object
Sets the text processor to use.
40 41 42 |
# File 'lib/opener/webservice/server.rb', line 40 def self.text_processor=(processor) @text_processor = processor end |
Instance Method Details
#/ ⇒ Object
Processes the input using a component.
Data can be submitted in two ways:
-
As regular POST fields
-
A single JSON object as the POST body
When submitting data, you can use the following fields (either as POST fields or as the fields of a JSON object):
| Field | Description | |:—————|:——————————————–| | input | The raw input text/KAF to process | | input_url | A URL to a document to download and process | | callbacks | An array of callback URLs | | error_callback | A URL to submit errors to | | request_id | A unique ID to associate with the document | | metadata | A custom metadata object to store in S3 |
In case of a JSON object the input body would look something like the following:
{"input": "Hello world, this is....", request_id: "123abc"}
78 79 80 |
# File 'lib/opener/webservice/server.rb', line 78 get '/' do erb :index end |
#analyze(options) ⇒ Array
Analyzes the input and returns an Array containing the output and content type.
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/opener/webservice/server.rb', line 184 def analyze() = InputSanitizer.new.( , self.class.accepted_params ) input = InputExtractor.new.extract() processor = self.class.text_processor.new() output = processor.run(input) if processor.respond_to?(:output_type) type = processor.output_type else type = :xml end return output, type end |
#analyze_async(options, request_id) ⇒ Object
Analyzes the input asynchronously.
209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/opener/webservice/server.rb', line 209 def analyze_async(, request_id) output, _ = analyze() submit_output(output, request_id, ) # Submit the error to the error callback, re-raise so Rollbar can also # report it. rescue Exception => error ErrorHandler.new.submit(error, request_id) if ['error_callback'] raise error end |
#async ⇒ Object
Runs the block in a separate thread. When running a test environment the block is instead yielded normally.
303 304 305 306 307 308 309 |
# File 'lib/opener/webservice/server.rb', line 303 def async if self.class.environment == :test yield else Thread.new { yield } end end |
#authenticate! ⇒ Object
Authenticates the current request.
287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/opener/webservice/server.rb', line 287 def authenticate! token = Configuration.authentication_token secret = Configuration.authentication_secret creds = {token => params[token], secret => params[secret]} response = HTTPClient.get(Configuration.authentication_endpoint, creds) unless response.ok? halt(403, "Authentication failed: #{response.body}") end end |
#json_input? ⇒ TrueClass|FalseClass
Returns ‘true` if the input data is in JSON, false otherwise
280 281 282 |
# File 'lib/opener/webservice/server.rb', line 280 def json_input? return request.content_type == 'application/json' end |
#params_from_json ⇒ Hash
Returns a Hash containing the parameters from a JSON payload. The keys of this Hash are returned as strings to prevent Symbol DOS attacks.
271 272 273 |
# File 'lib/opener/webservice/server.rb', line 271 def params_from_json return JSON.load(request.body.read) end |
#process_async(options) ⇒ Hash
Processes a request asynchronously, results are submitted to the next callback URL.
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/opener/webservice/server.rb', line 158 def process_async() request_id = ['request_id'] || SecureRandom.hex final_url = ['callbacks'].last Core::Syslog.info( "Processing asynchronous request with final URL #{final_url}", :request_id => request_id ) async { analyze_async(, request_id) } content_type :json return JSON.dump( :request_id => request_id, :output_url => "#{final_url}/#{request_id}" ) end |
#process_sync(options) ⇒ String
Processes a request synchronously, results are sent as the response upon completion.
143 144 145 146 147 148 149 |
# File 'lib/opener/webservice/server.rb', line 143 def process_sync() output, ctype = analyze() content_type(ctype) return output end |
#submit_output(output, request_id, options) ⇒ Object
Submits the output to the next callback URL.
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/opener/webservice/server.rb', line 229 def submit_output(output, request_id, ) callbacks = ['callbacks'].dup next_url = callbacks.shift # Re-use the old payload so that any extra data (e.g. metadata) is kept # in place. new_payload = .merge( 'callbacks' => callbacks, 'request_id' => request_id ) # Make sure we don't re-send this to the next component. new_payload.delete('input') if Configuration.output_bucket Core::Syslog.info( "Uploading output to s3://#{Configuration.output_bucket}", :request_id => request_id ) uploader = Uploader.new object = uploader.upload(request_id, output, ['metadata']) new_payload['input_url'] = object.url_for(:read, :expires => 3600) else new_payload['input'] = output end Core::Syslog.info( "Submitting output to #{next_url}", :request_id => request_id ) CallbackHandler.new.post(next_url, new_payload) end |