diff --git a/app/controllers/api/v1/webhooks_controller.rb b/app/controllers/api/v1/webhooks_controller.rb index e08c1cc090..d489014cf6 100644 --- a/app/controllers/api/v1/webhooks_controller.rb +++ b/app/controllers/api/v1/webhooks_controller.rb @@ -7,7 +7,8 @@ def index bot_name_to_class = { smooch: Bot::Smooch, keep: Bot::Keep, - fetch: Bot::Fetch + fetch: Bot::Fetch, + alegre: Bot::Alegre, } unless bot_name_to_class.has_key?(params[:name].to_sym) render_error('Bot not found', 'ID_NOT_FOUND', 404) and return diff --git a/app/models/bot/alegre.rb b/app/models/bot/alegre.rb index d8997e3a3f..d0b1e76aa3 100644 --- a/app/models/bot/alegre.rb +++ b/app/models/bot/alegre.rb @@ -6,6 +6,7 @@ class Error < ::StandardError end include AlegreSimilarity + include AlegreWebhooks # Text similarity models MEAN_TOKENS_MODEL = 'xlm-r-bert-base-nli-stsb-mean-tokens' @@ -491,7 +492,13 @@ def self.request_api(method, path, params = {}, query_or_body = 'body', retries response_body = response.body Rails.logger.info("[Alegre Bot] Alegre response: #{response_body.inspect}") ActiveRecord::Base.connection.reconnect! if RequestStore.store[:pause_database_connection] - JSON.parse(response_body) + parsed_response = JSON.parse(response_body) + if parsed_response.dig("queue") == 'audio__Model' && parsed_response.dig("body", "callback_url") != nil + redis = Redis.new(REDIS_CONFIG) + redis_response = redis.blpop("alegre:webhook:#{parsed_response.dig("body", "id")}", 120) + return JSON.parse(redis_response[1]) + end + parsed_response rescue StandardError => e if retries > 0 sleep 1 diff --git a/app/models/concerns/alegre_similarity.rb b/app/models/concerns/alegre_similarity.rb index d331405478..bc5d6faf61 100644 --- a/app/models/concerns/alegre_similarity.rb +++ b/app/models/concerns/alegre_similarity.rb @@ -19,7 +19,7 @@ def get_similar_items(pm) type = Bot::Alegre.get_pm_type(pm) Rails.logger.info "[Alegre Bot] [ProjectMedia ##{pm.id}] [Similarity 2/5] Type is #{type.blank? ? "blank" : type}" unless type.blank? - if !self.should_get_similar_items_of_type?('master', pm.team_id) || !self.should_get_similar_items_of_type?(type, pm.team_id) + if !Bot::Alegre.should_get_similar_items_of_type?('master', pm.team_id) || !Bot::Alegre.should_get_similar_items_of_type?(type, pm.team_id) Rails.logger.info "[Alegre Bot] [ProjectMedia ##{pm.id}] [Similarity 3/5] ProjectMedia cannot be checked for similar items" return {} else diff --git a/app/models/concerns/alegre_webhooks.rb b/app/models/concerns/alegre_webhooks.rb new file mode 100644 index 0000000000..4b71b2d582 --- /dev/null +++ b/app/models/concerns/alegre_webhooks.rb @@ -0,0 +1,26 @@ +class AlegreCallbackError < StandardError +end + +module AlegreWebhooks + extend ActiveSupport::Concern + + module ClassMethods + def valid_request?(request) + token = request.params['token'] || request.query_parameters['token'] + !token.blank? && token == CheckConfig.get('alegre_token') + end + + def webhook(request) + begin + doc_id = request.params.dig('data', 'requested', 'body', 'id') + raise 'Unexpected params format' if doc_id.blank? + redis = Redis.new(REDIS_CONFIG) + key = "alegre:webhook:#{doc_id}" + redis.lpush(key, request.params.to_json) + redis.expire(key, 1.day.to_i) + rescue StandardError => e + CheckSentry.notify(AlegreCallbackError.new(e.message), { alegre_response: request.params }) + end + end + end +end diff --git a/config/config.yml.example b/config/config.yml.example index ebbd6d49c9..3943600acd 100644 --- a/config/config.yml.example +++ b/config/config.yml.example @@ -11,7 +11,6 @@ development: &default elasticsearch_index: elasticsearch_log: true elasticsearch_sync: false - # WARNING For production, don't use a wildcard: set the allowed domains explicitly as a regular expression, e.g. # '(https?://.*\.?(meedan.com|meedan.org))' allowed_origins: '.*' diff --git a/test/controllers/webhooks_controller_test.rb b/test/controllers/webhooks_controller_test.rb index f91de407b7..e809d0001d 100644 --- a/test/controllers/webhooks_controller_test.rb +++ b/test/controllers/webhooks_controller_test.rb @@ -232,4 +232,24 @@ def setup assert_equal '200', response.code assert_match /ignored/, response.body end + + test "should process Alegre webhook" do + CheckSentry.expects(:notify).never + redis = Redis.new(REDIS_CONFIG) + redis.del('foo') + payload = { 'action' => 'audio', 'data' => { 'requested' => { 'body' => { 'id' => 'foo', 'context' => { 'project_media_id' => random_number } } } } } + assert_nil redis.lpop('alegre:webhook:foo') + + post :index, params: { name: :alegre, token: CheckConfig.get('alegre_token') }.merge(payload) + response = JSON.parse(redis.lpop('alegre:webhook:foo')) + assert_equal 'foo', response.dig('data', 'requested', 'body', 'id') + + travel_to Time.now.since(2.days) + assert_nil redis.lpop('alegre:webhook:foo') + end + + test "should report error if can't process Alegre webhook" do + CheckSentry.expects(:notify).once + post :index, params: { name: :alegre, token: CheckConfig.get('alegre_token') }.merge({ foo: 'bar' }) + end end diff --git a/test/models/bot/alegre_2_test.rb b/test/models/bot/alegre_2_test.rb index 804ce5e991..120b3c25fc 100644 --- a/test/models/bot/alegre_2_test.rb +++ b/test/models/bot/alegre_2_test.rb @@ -315,6 +315,16 @@ def teardown RequestStore.store[:pause_database_connection] = false end + test "should block calls on redis blpop for audio request" do + stubbed_response = Net::HTTPSuccess.new(1.0, '200', 'OK') + stubbed_response.stubs(:body).returns({"queue" => "audio__Model", "body" => {"id" => "123", "callback_url" => "http://example.com/callback"}}.to_json) + Net::HTTP.any_instance.stubs(:request).returns(stubbed_response) + Redis.any_instance.stubs(:blpop).with("alegre:webhook:123", 120).returns(["alegre:webhook:123", {"tested" => true}.to_json]) + assert_equal Bot::Alegre.request_api('get', '/audio/similarity/', @params, 'body'), {"tested" => true} + Net::HTTP.any_instance.unstub(:request) + Redis.any_instance.unstub(:blpop) + end + test "should get items with similar title" do create_verification_status_stuff RequestStore.store[:skip_cached_field_update] = false