sp-job 0.1.17

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.
@@ -0,0 +1,36 @@
1
+ # Sp::Job
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/sp/job`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'sp-job'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install sp-job
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/sp-job.
36
+
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ Dir.glob('lib/tasks/*.rake').each { |r| load r }
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.17
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "sp/job"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright (c) 2011-2017 Cloudware S.A. All rights reserved.
4
+ #
5
+ # This file is part of sp-job.
6
+ #
7
+ # sp-job is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # sp-job is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with sp-job. If not, see <http://www.gnu.org/licenses/>.
19
+ #
20
+ # encoding: utf-8
21
+ #
22
+ require 'bundler/setup'
23
+ require 'sp/job'
24
+ require 'sp/job'
25
+ require 'redis'
26
+ require 'backburner'
27
+ require 'optparse'
28
+ require 'sp/job/common'
29
+ require 'syslog/logger'
30
+ extend SP::Job::Common
31
+
32
+ begin
33
+ $args = {}
34
+ $config = {}
35
+ $args[:redis] = '127.0.0.1:6379'
36
+ $args[:beanstalkd] = '127.0.0.1:11300'
37
+ $args[:validity] = 7200
38
+ $args[:ttr] = 3600
39
+
40
+ #
41
+ # Parse command line arguments
42
+ #
43
+ $option_parser = OptionParser.new do |opts|
44
+ opts.banner = "Usage: #{$PROGRAM_NAME} ARGS"
45
+ opts.on('-r', '--redis=HOST:PORT' , "Hostname and port of the redis server (default: '#{$args[:redis]}')") { |v| $args[:redis] = v }
46
+ opts.on('-b', '--beanstalkd=HOST:PORT', "Hostname and port of the beanstalkd server (default: '#{$args[:beanstalkd]}')") { |v| $args[:beanstalkd] = v }
47
+ opts.on('-V', '--validity=SECS' , "job validty in seconds") { |v| $args[:validity] = v }
48
+ opts.on('-t', '--tube=TUBE' , "beanstalkd tube name") { |v| $args[:tube] = v }
49
+ opts.on('-i', '--sid=SERVICEID' , "service id on redis") { |v| $config[:service_id] = v }
50
+ opts.on('-v', '--ttr=SECS' , "job ttr time to run in seconds") { |v| $args[:ttr] = v }
51
+ opts.on('-l', '--log=LOGFILE' , "path to log file (default: '#{$args[:log_file]}')") { |v| $args[:log_file] = File.expand_path(v) }
52
+ opts.on('-d', '--debug' , "developer mode: log to stdout and print job") { $args[:debug] = true }
53
+ end
54
+ $option_parser.parse!
55
+
56
+ raise "Tube must be specified with --tube!!!" if $args[:tube].nil?
57
+ raise "Service id must be specified with --sid!!!" if $config[:service_id].nil?
58
+
59
+ $redis = Redis.new(:host => $args[:redis].split(':')[0], :port => $args[:redis].split(':')[1], :db => 0)
60
+ $beaneater = Beaneater.new $args[:beanstalkd]
61
+ job = { }
62
+ ARGV.each do |arg|
63
+ key, value = arg.to_s.split('=')
64
+ job[key.to_sym] = value
65
+ end
66
+ submit_job(job: job, tube: $args[:tube], ttr: $args[:ttr], validity: $args[:validity])
67
+ rescue => e
68
+ STDERR.puts e
69
+ STDERR.puts e.backtrace
70
+ sys_log = Syslog::Logger.new $PROGRAM_NAME
71
+ sys_log.error "#{e} #{e.backtrace}"
72
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,45 @@
1
+ #
2
+ # Copyright (c) 2011-2016 Cloudware S.A. All rights reserved.
3
+ #
4
+ # This file is part of sp-job.
5
+ #
6
+ # sp-job is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # sp-job is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with sp-job. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ #require 'byebug'
21
+ require 'awesome_print'
22
+ require 'rollbar'
23
+ require 'redis'
24
+ require 'backburner'
25
+ require 'json'
26
+ require 'fileutils'
27
+ require 'concurrent'
28
+ require 'optparse'
29
+ require 'os'
30
+ require 'pg'
31
+ require 'sp-duh'
32
+ require 'oauth2'
33
+ require 'oauth2-client'
34
+ require 'curb'
35
+ require 'rails'
36
+ require 'erb'
37
+ require 'ostruct'
38
+ require 'json'
39
+ require 'mail'
40
+
41
+ require 'sp/job'
42
+ require 'sp/job/engine'
43
+ require 'sp/job/version'
44
+ require 'sp/job/worker'
45
+ require 'sp/job/broker_oauth2_client'
@@ -0,0 +1,24 @@
1
+ #
2
+ # Copyright (c) 2011-2017 Cloudware S.A. All rights reserved.
3
+ #
4
+ # This file is part of sp-job.
5
+ #
6
+ # sp-job is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # sp-job is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with sp-job. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ module SP
21
+ module Job
22
+ MODULE_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
23
+ end
24
+ end
@@ -0,0 +1,205 @@
1
+ #
2
+ # Copyright (c) 2011-2017 Cloudware S.A. All rights reserved.
3
+ #
4
+ # This file is part of sp-job.
5
+ #
6
+ # sp-job is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # sp-job is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with sp-job. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+ # encoding: utf-8
20
+ #
21
+ require 'sp/job/pg_connection'
22
+ require 'roadie'
23
+
24
+ #
25
+ # Initialize global data needed for configuration
26
+ #
27
+ $prefix = OS.mac? ? '/usr/local' : '/'
28
+ $rollbar = false
29
+ $bury = false
30
+ $min_progress = 3 # TODO to config??
31
+ $args = {
32
+ stdout: false,
33
+ log_level: 'info',
34
+ program_name: File.basename($PROGRAM_NAME, File.extname($PROGRAM_NAME)),
35
+ config_file: File.join($prefix, 'etc', File.basename($PROGRAM_NAME, File.extname($PROGRAM_NAME)), 'conf.json'),
36
+ log_file: File.join($prefix, 'var', 'log', 'jobs', "#{File.basename($PROGRAM_NAME, File.extname($PROGRAM_NAME))}.log")
37
+ }
38
+
39
+ #
40
+ # Parse command line arguments
41
+ #
42
+ $option_parser = OptionParser.new do |opts|
43
+ opts.banner = "Usage: #{$PROGRAM_NAME} ARGS"
44
+ opts.on('-c', '--config=CONFIG.JSON', "path to json configuration file (default: '#{$args[:config_file]}')") { |v| $args[:config_file] = File.expand_path(v) }
45
+ opts.on('-l', '--log=LOGFILE' , "path to log file (default: '#{$args[:log_file]}')") { |v| $args[:log_file] = File.expand_path(v) }
46
+ opts.on('-d', '--debug' , "developer mode: log to stdout and print job") { $args[:debug] = true }
47
+ opts.on('-v', '--log_level=LEVEL' , "Log level DEBUG, INFO, WARN, ERROR, FATAL") { |v| $args[:log_level] = v }
48
+ end
49
+ $option_parser.parse!
50
+
51
+ #
52
+ # Read configuration
53
+ #
54
+ $config = JSON.parse(File.read(File.expand_path($args[:config_file])), symbolize_names: true)
55
+
56
+ #
57
+ # Configure rollbar
58
+ #
59
+ unless $config[:rollbar].nil?
60
+ $rollbar = true
61
+ Rollbar.configure do |config|
62
+ config.access_token = $config[:rollbar][:token] if $config[:rollbar][:token]
63
+ config.environment = $config[:rollbar][:environment] if $config[:rollbar] && $config[:rollbar][:environment]
64
+ end
65
+ end
66
+
67
+ #
68
+ # Configure backburner queue
69
+ #
70
+ Backburner.configure do |config|
71
+
72
+ config.beanstalk_url = "beanstalk://#{$config[:beanstalkd][:host]}:#{$config[:beanstalkd][:port]}"
73
+ config.on_error = lambda { |e|
74
+ if $exception_reported == false
75
+ $exception_reported == true
76
+ update_progress(status: 'error', message: e)
77
+ end
78
+ if $rollbar
79
+ Rollbar.error(e)
80
+ end
81
+ catch_fatal_exceptions(e)
82
+ }
83
+ #config.priority_labels = { :custom => 50, :useless => 1000 }
84
+ #config.max_job_retries = 0 # default 0 retries
85
+ #config.retry_delay = 5 # default 5 seconds
86
+ #config.default_priority = 65536
87
+ config.retry_delay_proc = lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 3) }
88
+ config.respond_timeout = 120
89
+ config.default_worker = SP::Job::Worker
90
+ config.logger = $args[:debug] ? Logger.new(STDOUT) : Logger.new($args[:log_file])
91
+ config.logger.formatter = proc do |severity, datetime, progname, msg|
92
+ date_format = datetime.strftime("%Y-%m-%d %H:%M:%S")
93
+ "[#{date_format}] #{severity}: #{msg}\n"
94
+ end
95
+ if $args[:log_level].nil?
96
+ config.logger.level = Logger::INFO
97
+ else
98
+ case $args[:log_level].upcase
99
+ when 'DEBUG'
100
+ config.logger.level = Logger::DEBUG
101
+ when 'INFO'
102
+ config.logger.level = Logger::INFO
103
+ when 'WARN'
104
+ config.logger.level = Logger::WARN
105
+ when 'ERROR'
106
+ config.logger.level = Logger::ERROR
107
+ when 'FATAL'
108
+ config.logger.level = Logger::FATAL
109
+ else
110
+ config.logger.level = Logger::INFO
111
+ end
112
+ end
113
+ config.logger.datetime_format = "%Y-%m-%d %H:%M:%S"
114
+ config.primary_queue = $args[:program_name]
115
+ config.reserve_timeout = nil
116
+ config.job_parser_proc = lambda { |body|
117
+ rv = Hash.new
118
+ rv[:args] = [JSON.parse(body, :symbolize_names => true)]
119
+ rv[:class] = rv[:args][0][:tube] || $args[:program_name]
120
+ rv
121
+ }
122
+ end
123
+
124
+ if $config[:mail]
125
+ Mail.defaults do
126
+ delivery_method :smtp, {
127
+ :address => $config[:mail][:smtp][:address],
128
+ :port => $config[:mail][:smtp][:port].to_i,
129
+ :domain => $config[:mail][:smtp][:domain],
130
+ :user_name => $config[:mail][:smtp][:user_name],
131
+ :password => $config[:mail][:smtp][:password],
132
+ :authentication => $config[:mail][:smtp][:authentication],
133
+ :enable_starttls_auto => $config[:mail][:smtp][:enable_starttls_auto]
134
+ }
135
+ end
136
+ end
137
+
138
+ #
139
+ # Monkey patches to keep the tube name as plain vannila job name
140
+ #
141
+ module Backburner
142
+ module Helpers
143
+
144
+ def expand_tube_name (tube)
145
+ tube
146
+ end
147
+
148
+ end
149
+
150
+ module Logger
151
+ def log_job_begin(name, args)
152
+ log_info "Work job #{name}"
153
+ @job_started_at = Time.now
154
+ end
155
+ end
156
+
157
+ class Job
158
+ # Processes a job and handles any failure, deleting the job once complete
159
+ #
160
+ # @example
161
+ # @task.process
162
+ #
163
+ def process
164
+ # Invoke before hook and stop if false
165
+ res = @hooks.invoke_hook_events(job_class, :before_perform, *args)
166
+ unless res
167
+ task.delete
168
+ return false
169
+ end
170
+ # Execute the job
171
+ @hooks.around_hook_events(job_class, :around_perform, *args) do
172
+ # We subtract one to ensure we timeout before beanstalkd does, except if:
173
+ # a) ttr == 0, to support never timing out
174
+ # b) ttr == 1, so that we don't accidentally set it to never time out
175
+ # NB: A ttr of 1 will likely result in race conditions between
176
+ # Backburner and beanstalkd and should probably be avoided
177
+ timeout_job_after(task.ttr > 1 ? task.ttr - 1 : task.ttr) { job_class.perform(*args) }
178
+ end
179
+ task.delete
180
+ # Invoke after perform hook
181
+ @hooks.invoke_hook_events(job_class, :after_perform, *args)
182
+ rescue => e
183
+ @hooks.invoke_hook_events(job_class, :on_failure, e, *args)
184
+ raise e
185
+ end
186
+ end
187
+ end
188
+
189
+ # Mix-in the mix-in in the script so that we can use the Common module functions
190
+ require 'sp/job/common'
191
+ extend SP::Job::Common
192
+
193
+ #
194
+ # Now create the global data needed by the mix-in methods
195
+ #
196
+ $connected = false
197
+ $job_status = {}
198
+ $validity = 2
199
+ $redis = Redis.new(:host => $config[:redis][:host], :port => $config[:redis][:port], :db => 0)
200
+ $beaneater = Beaneater.new "#{$config[:beanstalkd][:host]}:#{$config[:beanstalkd][:port]}"
201
+ $check_db_life_span = false
202
+ $status_dirty = false
203
+ if $config[:postgres] && $config[:postgres][:conn_str]
204
+ $pg = ::SP::Job::PGConnection.new(owner: 'back_burner', config: $config[:postgres])
205
+ end
@@ -0,0 +1,372 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # encoding: utf-8
4
+ #
5
+ # Copyright (c) 2017 Cloudware S.A. Allrights reserved
6
+ #
7
+ # Helper to obtain tokens to access toconline API's.
8
+ #
9
+
10
+ require 'sp/job/jsonapi_error'
11
+
12
+ module SP
13
+ module Job
14
+
15
+ class Broker
16
+
17
+ #
18
+ # Helper class that defined an 'i18n' message.
19
+ #
20
+ class I18N
21
+
22
+ private
23
+
24
+ @key
25
+ @args
26
+
27
+ public
28
+
29
+ attr_accessor :key
30
+ attr_accessor :args
31
+
32
+ def initialize (key:, args:)
33
+ @key = key
34
+ @args = args
35
+ end
36
+
37
+ end
38
+
39
+
40
+ #
41
+ # Helper class that defined an 'Broker' error.
42
+ #
43
+ class Error < ::SP::Job::JSONAPIError
44
+
45
+ def initialize (i18n:, code:, internal:)
46
+ super(code: code, internal: internal)
47
+ @i18n = i18n
48
+ end
49
+
50
+ end
51
+
52
+ #
53
+ # Helper class that defined an 'Not Implemented' error.
54
+ #
55
+ class NotImplementedError < Error
56
+
57
+ def initialize (i18n:, internal:)
58
+ super(i18n: i18n, code: 501, internal: internal)
59
+ end
60
+
61
+ end
62
+
63
+ #
64
+ # Helper class that defined an 'Bard Request' error.
65
+ #
66
+ class BadRequest < Error
67
+
68
+ def initialize (i18n:, internal:)
69
+ super(i18n: i18n, code: 400, internal: internal)
70
+ end
71
+
72
+ end
73
+
74
+ #
75
+ # Helper class that defined an 'Internal Error' error.
76
+ #
77
+ class InternalError < Error
78
+
79
+ def initialize (i18n:, internal:)
80
+ super(i18n: i18n, code: 500, internal: internal)
81
+ end
82
+
83
+ end
84
+
85
+ #
86
+ # Helper class that defined an 'Unauthorized' error.
87
+ #
88
+ class Unauthorized < Error
89
+
90
+ def initialize (i18n:, internal:)
91
+ super(i18n: i18n, code: 401, internal: internal)
92
+ end
93
+
94
+ end
95
+
96
+ #
97
+ #
98
+ #
99
+ class OAuth2
100
+
101
+ private
102
+
103
+ @service_id = nil
104
+ @client = nil
105
+ @redis = nil
106
+
107
+ public
108
+
109
+ def initialize (service_id:, config:, redis: nil)
110
+ @service_id = service_id
111
+ @client = ::SP::Job::BrokerOAuth2Client.new(
112
+ protocol: config[:protocol],
113
+ host: config[:host],
114
+ port: config[:port],
115
+ client_id: config[:client_id],
116
+ client_secret: config[:client_secret],
117
+ redirect_uri: config[:redirect_uri],
118
+ scope: config[:scope],
119
+ options: {}
120
+ )
121
+ @redis = redis
122
+ end
123
+
124
+ #
125
+ # Obtain an 'access' and a 'refresh' token.
126
+ #
127
+ # @param scope
128
+ #
129
+ def authorize (scope: nil, fields: nil)
130
+ # obtain an 'authorization code'
131
+ ac_response = @client.get_authorization_code(
132
+ a_redirect_uri = nil,
133
+ a_scope = scope
134
+ )
135
+ # got a valid 'authorization code'?
136
+ if ac_response[:oauth2].has_key?(:code)
137
+ # got fields?
138
+ if nil != fields
139
+ # prepare redis arguments: field value, [field value, ...]
140
+ array = []
141
+ fields.each do |k,v|
142
+ array << k.to_s
143
+ array << v
144
+ end
145
+ $redis.hmset("#{@service_id}:oauth:authorization_code:#{ac_response[:oauth2][:code]}",
146
+ array,
147
+ 'patched_by', 'toconline-session'
148
+ )
149
+ end
150
+ # exchange it for a 'access' and a 'refresh' token
151
+ at_response = @client.exchange_auth_code_for_token(
152
+ ac_response[:oauth2][:code]
153
+ )
154
+ # return 'oauth2' at object
155
+ return at_response
156
+ else
157
+ # return 'oauth2' ac object
158
+ return ac_response
159
+ end
160
+ end
161
+
162
+ #
163
+ # Refresh an access token.
164
+ #
165
+ # @param scope
166
+ # @param old
167
+ #
168
+ def refresh (scope: nil, old: nil)
169
+ at_response = @client.refresh_access_token(
170
+ a_refresh_token = old[:refresh_token],
171
+ a_scope = scope
172
+ )
173
+ if true == at_response[:oauth2].has_key?(:error)
174
+ return at_response
175
+ end
176
+ # no error, delete old tokens
177
+ if nil == old
178
+ # return oauth response
179
+ return at_response
180
+ end
181
+ # old tokens provided: remove them from redis
182
+ if nil == @redis || nil == @service_id
183
+ raise InternalError.new(i18n: nil, internal: nil)
184
+ end
185
+ # delete old tokens from redis
186
+ @redis.multi do |multi|
187
+ if nil != old[:access_token]
188
+ multi.del("#{@service_id}:oauth:access_token:#{old[:access_token]}")
189
+ end
190
+ if nil != old[:refresh_token]
191
+ multi.del("#{@service_id}:oauth:refresh_token:#{old[:refresh_token]}")
192
+ end
193
+ end
194
+ # return oauth response
195
+ return at_response
196
+ end
197
+
198
+ #
199
+ # Patch a pair of tokens, by generating new ones
200
+ #
201
+ # @param access_token
202
+ # @param refresh_token
203
+ # @param fields
204
+ #
205
+ def patch (access_token:, refresh_token:, fields:)
206
+ if nil == @redis || nil == @service_id
207
+ raise InternalError.new(i18n: nil, internal: nil)
208
+ end
209
+ # generate new pair, based on provided refresh_token
210
+ at_response = @client.refresh_access_token(
211
+ a_refresh_token = refresh_token,
212
+ a_scope = nil # keep current scope
213
+ )
214
+ if at_response[:oauth2].has_key?(:error)
215
+ return at_response
216
+ end
217
+ # prepare redis arguments: field value, [field value, ...]
218
+ array = []
219
+ fields.each do |k,v|
220
+ array << k.to_s
221
+ array << v
222
+ end
223
+ # patch new tokens
224
+ @redis.multi do |multi|
225
+ multi.hmset("#{@service_id}:oauth:refresh_token:#{at_response[:oauth2][:refresh_token]}",
226
+ array,
227
+ 'patched_by', 'toconline-session'
228
+ )
229
+ multi.hmset("#{@service_id}:oauth:access_token:#{at_response[:oauth2][:access_token]}",
230
+ array,
231
+ 'patched_by', 'toconline-session'
232
+ )
233
+ end
234
+ # delete old tokens from redis
235
+ @redis.multi do |multi|
236
+ multi.del("#{@service_id}:oauth:access_token:#{access_token}")
237
+ multi.del("#{@service_id}:oauth:refresh_token:#{refresh_token}")
238
+ end
239
+ # return oauth response
240
+ return at_response
241
+ end
242
+
243
+ #
244
+ # Remove a pair of tokens from redis.
245
+ #
246
+ # @param access
247
+ # @param refresh
248
+ #
249
+ def dispose (access:, refresh:)
250
+ if nil == @redis || nil == @service_id
251
+ raise InternalError.new(i18n: nil, internal: nil)
252
+ end
253
+
254
+ if refresh.nil?
255
+ refresh = @redis.hget("#{@service_id}:oauth:access_token:#{access}",'refresh_token')
256
+ end
257
+
258
+ # delete tokens from redis
259
+ @redis.multi do |multi|
260
+ multi.del("#{@service_id}:oauth:access_token:#{access}")
261
+ multi.del("#{@service_id}:oauth:refresh_token:#{refresh}")
262
+ end
263
+ #
264
+ nil
265
+ end
266
+
267
+ end
268
+
269
+ #
270
+ #
271
+ #
272
+ class Job
273
+
274
+ #
275
+ #
276
+ #
277
+ attr_accessor :oauth2
278
+ attr_accessor :output
279
+
280
+ #
281
+ #
282
+ #
283
+ def initialize (config:)
284
+ if nil != config && nil != config[:oauth2]
285
+ @oauth2 = OAuth2.new(service_id: config[:service_id], config: config[:oauth2], redis: config[:redis])
286
+ else
287
+ @oauth2 = nil
288
+ end
289
+ @output = {
290
+ :action => "response",
291
+ :content_type => "application/json",
292
+ :status_code => 400,
293
+ :response => nil
294
+ }
295
+ end
296
+
297
+ #
298
+ # Finalize the job response.
299
+ #
300
+ # @param response
301
+ # @param content_type
302
+ # @param status_code
303
+ #
304
+ # @return
305
+ #
306
+ def finalized (response:, content_type: 'application/json', status_code: 200)
307
+ @output[:response] = response
308
+ @output[:content_type] = content_type
309
+ @output[:status_code] = status_code
310
+ @output
311
+ end
312
+
313
+ #
314
+ # Perform an OAuth2 request, catch errors
315
+ # and convertem them to a common result hash.
316
+ #
317
+ # @param callback
318
+ #
319
+ def call(*callback)
320
+ begin
321
+ @output = yield
322
+ rescue ::SP::Job::BrokerOAuth2Client::InvalidEmailOrPassword => invalid_password
323
+ @output[:status_code] = 403
324
+ @output[:content_type], @output[:response] = Error.new(i18n: nil, code: @output[:status_code],
325
+ internal: invalid_password.as_hash[:oauth2]
326
+ ).content_type_and_body()
327
+ rescue ::SP::Job::BrokerOAuth2Client::AccessDenied => acccess_denied
328
+ @output[:status_code] = 403
329
+ @output[:content_type], @output[:response] = Error.new(i18n: nil, code: @output[:status_code],
330
+ internal: acccess_denied.as_hash[:oauth2]
331
+ ).content_type_and_body()
332
+ rescue ::SP::Job::BrokerOAuth2Client::UnauthorizedUser => unauthorized_user
333
+ @output[:status_code] = 401
334
+ @output[:content_type], @output[:response] = Error.new(i18n: nil, code: @output[:status_code],
335
+ internal: unauthorized_user.as_hash[:oauth2]
336
+ ).content_type_and_body()
337
+ rescue ::SP::Job::BrokerOAuth2Client::InternalError => internal_error
338
+ @output[:status_code] = 500
339
+ @output[:content_type], @output[:response] = Error.new(i18n: nil, code: @output[:status_code],
340
+ internal: internal_error.as_hash[:oauth2]
341
+ ).content_type_and_body()
342
+ rescue ::SP::Job::BrokerOAuth2Client::Error => error
343
+ @output[:status_code] = 500
344
+ @output[:content_type], @output[:response] = Error.new(i18n: nil, code: @output[:status_code],
345
+ internal: error.as_hash[:oauth2]
346
+ ).content_type_and_body()
347
+ rescue NotImplementedError => broker_not_implemented
348
+ @output[:status_code] = broker_not_implemented.code
349
+ @output[:content_type], @output[:response] = b_not_implemented.content_type_and_body()
350
+ rescue BadRequest => broker_bad_request
351
+ @output[:status_code] = broker_bad_request.code
352
+ @output[:content_type], @output[:response] = broker_bad_request.content_type_and_body()
353
+ rescue InternalError => broker_internal_error
354
+ @output[:status_code] = broker_internal_error.code
355
+ @output[:content_type], @output[:response] = broker_internal_error.content_type_and_body()
356
+ rescue Error => broker_error
357
+ @output[:status_code] = broker_error.code
358
+ @output[:content_type], @output[:response] = broker_error.content_type_and_body()
359
+ rescue Exception => e
360
+ internal_error = InternalError.new(i18n: nil, internal: nil)
361
+ @output[:status_code] = internal_error.code
362
+ @output[:content_type], @output[:response] = internal_error.content_type_and_body()
363
+ end
364
+ @output
365
+ end
366
+
367
+ end
368
+
369
+ end # end class 'Broker'
370
+
371
+ end # module Job
372
+ end# module SP