I have an rails app that sends a webhook (localhost:3000) with data. Before, I used a online webhook monitor (Pipedream) but I want now to build a second rails app to receive the data (localhost:3000 -> localhost:3300). I've created a route /webhook/receive and the controller for it but get nothing from localhost:3000.
What I'm doing wrong?
Thats on my first app:
def send_create_webhook(webhook_url)
company = User.find_by(email: 'test1#test.de').company_account
webhook_url = 'tcp://0.0.0.0:3300/data/receive'
SendWebhookJob.perform_later(webhook_url, {
subscription: {
type: "#{#tool.title} successfully subscribed",
date: "#{Time.zone.now}",
subscribed: Time.now.strftime("%Y-%d-%m %H:%M:%S %Z"),
user: {
company: "#{company.title}",
name: current_user.name,
email: current_user.email,
}
}
})
end
class SendWebhookJob < ApplicationJob
queue_as :default
WEBHOOK_RETRIES = 3
def perform(webhook_url, data, retry_count = 0)
p "COUNT #{retry_count} : #{Time.zone.now} : Sending data to #{webhook_url}"
# make a request
begin
response = HTTP.post(webhook_url, json:data)
successful = response.code == 200
rescue StandardError => e
successful = false
end
if successful
p "SUCCESSFUL WEBHOOK"
elsif retry_count < WEBHOOK_RETRIES
wait = 2 ** retry_count
SendWebhookJob.set(wait: wait.seconds).perform_later(webhook_url, data,retry_count + 1)
end
end
end
The second app I just created:
resources :data do
get :receive
post :receive
end
def DataController < ApplicationController
require "http"
def receive
response = HTTP.get('tcp://0.0.0.0:3000')
end
end
Related
I'm working on a web application that frequently access simulation data on a remote server. I want to create test for errors handling that might happen during these request.
The problem I currently have is I cannot seems to mock a request with my ssh_with_stderr method. The ssh method works fine.
This the code I'm trying to test:
# app/jobs/zip_files_sync_job.rb
class ZipFilesSyncJob < ApplicationJob
queue_as :default
discard_on ActiveJob::DeserializationError
def perform(simulation)
simulation.zip_files.each do |f|
if f.path.nil? && f.created_at < 6.hours.ago
f.state = 'error'
f.save!
next
end
next if f.path.nil?
_, errors = simulation.server.ssh_with_stderr("ls #{f.path.shellescape}")
if errors.blank?
f.size = f.simulation.server.ssh("stat -c %s #{f.path.shellescape}")
f.state = 'ready' if f.size.to_i.positive?
elsif f.state == 'ready' && errors.present?
f.state = 'error'
elsif f.state == 'zipping' && errors.present? && f.created_at < 6.hours.ago
f.state = 'error'
end
f.save!
end
end
end
And this is what I want to test:
# spec/jobs/zip_files_sync_job_spec.rb
require 'rails_helper'
RSpec.describe ZipFilesSyncJob, type: :job do
let(:private_group) { Group::PRIVATE }
let(:user) { FactoryBot.create :user }
let(:server) { FactoryBot.create :server, user: user, external_storage: false }
let(:simulation) { FactoryBot.create :simulation, user: user, group: private_group, server: server }
let(:zip_file) { FactoryBot.create :zip_file, simulation: simulation, path: 'test/zip_file', state: 'pending', size: '100' }
let(:zip_file_no_path) { FactoryBot.create :zip_file, simulation: simulation, path: nil, created_at: 10.hours.ago, state: 'pending' }
let(:ssh_connection) { double('net-ssh') }
before do
zip_file_no_path
allow(Net::SSH).to receive(:start).and_yield(ssh_connection)
end
def perform_zip_file_sync(zip_file)
perform_enqueued_jobs do
ZipFilesSyncJob.perform_now(simulation)
end
zip_file.reload
yield
allow(Net::SSH).to receive(:start).and_call_original
end
describe '#perform' do
include ActiveJob::TestHelper
#################################
##### This test works fine #####
#################################
context 'with no errors' do
before do
zip_file
end
it 'it will change the state to ready' do
allow(Net::SSH).to receive(:start).and_return('144371201')
perform_zip_file_sync(zip_file) do
expect(zip_file.state).to eq 'ready'
end
end
end
#############################################################################
##### This test fails because it does not return on the ssh_with_stderr #####
#############################################################################
context 'with errors' do
it 'will change the state to error' do
allow(Net::SSH).to receive(:start).and_return("[' ', 'Error with connection']")
perform_enqueued_jobs do
ZipFilesSyncJob.perform_now(simulation)
end
zip_file.reload
expect(zip_file.state).to eq 'error'
end
end
end
end
This the the code for the server connection. It uses the net-ssh gem
# app/models/server.rb
Class Server < ApplicationRecord
def ssh(command, storage = true, &block)
Net::SSH.start(hostname, username, port: port, keys: ["key"], non_interactive: true, timeout: 1) do |ssh|
ssh.exec! "cd #{folder.shellescape}; #{command}", &block
end
end
def ssh_with_stderr(command)
#output = ""
#errors = ""
begin
Net::SSH.start(hostname, username, port: port, keys: ["key"], non_interactive: true, timeout: 1) do |ssh|
ssh.exec! "cd #{folder.shellescape}; #{command}" do |_ch, stream, data|
if stream == :stderr
#errors += data
else
#output += data
end
end
end
rescue Net::SSH::Exception, Errno::ECONNREFUSED, Errno::EINVAL, Errno::EADDRNOTAVAIL => e
#output = nil
#errors = e.message
end
[#output, #errors]
end
With this mock
allow(Net::SSH).to receive(:start).and_return("[' ', 'Error with connection']")
the ssh_with_stderr looks like
def ssh_with_stderr(command)
#output = ""
#errors = ""
begin
[' ', 'Error with connection']
rescue Net::SSH::Exception, Errno::ECONNREFUSED, Errno::EINVAL, Errno::EADDRNOTAVAIL => e
#output = nil
#errors = e.message
end
[#output, #errors]
end
So it always returns ["",""] , and checking errors.blank? always positive.
Try to mock Net::SSH with and_raise instead of and_return, something like
allow(Net::SSH).to receive(:start).and_raise(Errno::ECONNREFUSED, "Error with connection")
I am using Zapier to search some information in google sheets. I used Webhocks to send a GET to his server with a JSON information. The response of GET is an "OK" and I can't custom this.
So, they will execute a task, find what a I want and return a value, but the response must be a GET in my server, and I don't know how to intercept this response in my route.
I'm trying to study Rails Rack to intercept de request in my app, but I don't know how to send the response to the event that sent the first GET.
How is my middleware:
class DeltaLogger
def initialize app
#app = app
end
def call env
Rails.logger.debug "#{env['QUERY_STRING']}"
#status, #headers, #response = #app.call(env)
[#status, #headers, #response]
end
end
Thanks!
Example
So, to get the value returned from Zapier, I created two routes and a global class cache.
class Zapier
require 'httparty'
def initialize
#answer = ""
#id = 0
end
def request(uri, task)
last_event = Event.last
puts last_event.inspect
if last_event.nil?
last_id = 0
else
last_id = last_event.event_id
end
event_id = last_id + 1
Event.find_or_create_by(event_id: event_id)
result = HTTParty.post(uri.to_str,
:body => {id: event_id, task: task}.to_json,
:headers => {'content-Type' => 'application/json'})
#answer = ""
#id = event_id
end
def response(event_id, value)
if event_id != #id
#answer = ""
else
#answer = value
end
end
def get_answer
#answer
end
end
And my controller:
class ZapierEventsController < ApplicationController
require 'zapier_class'
before_action :get_task, only: [:get]
before_action :get_response, only: [:set]
##zapier ||= Zapier.new
def get
##zapier.request('https://hooks.zapier.com',#task)
sleep 10 #Wait for response
#value = ##zapier.get_answer
render json: { 'value': #value }, status:
end
def set
##zapier.response(#id, #value)
render json: { 'status': 'ok' }, status: 200
end
def get_task
#task = params["task"]
end
def get_response
#id = Integer(params["id"])
#value = params["value"]
end
end
Now i have to make a Task Mananger
I am following the Learn Enough Action Cable tutorial. I have gotten to the end of section 6, adding #mentions to create notifications to the mentioned user. One of the exercises is to append the sending user's name to the alert text. So far I am only getting "#undefined". I'm guessing data.mention.username is not the correct call to append. In the console to pull the username from a message I did User.find_by(id: Message.last.user_id).username, but I don't know how to translate that to working coffeescript.
room.coffee
App.room = App.cable.subscriptions.create "RoomChannel",
connected: ->
# Called when the subscription is ready for use on the server
disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
alert("You have a new mention from #" + data.mention.username) if data.mention
if (data.message && !data.message.blank?)
$('#messages-table').append data.message
scroll_bottom()
$(document).on 'turbolinks:load', ->
submit_message()
scroll_bottom()
submit_message = () ->
$('#message_content').on 'keydown', (event) ->
if event.keyCode is 13 && !event.shiftKey
$('input').click()
event.target.value = ""
event.preventDefault()
scroll_bottom = () ->
$('#messages').scrollTop($('#messages')[0].scrollHeight)
messages_controller.rb
class MessagesController < ApplicationController
before_action :logged_in_user
before_action :get_messages
def index
end
def create
message = current_user.messages.build(message_params)
if message.save
ActionCable.server.broadcast 'room_channel',
message: render_message(message)
message.mentions.each do |mention|
ActionCable.server.broadcast "room_channel_user_#{mention.id}",
mention: true
end
end
end
private
def get_messages
#messages = Message.for_display
#message = current_user.messages.build
end
def message_params
params.require(:message).permit(:content)
end
def render_message(message)
render(partial: 'message', locals: { message: message })
end
end
message.rb
class Message < ApplicationRecord
belongs_to :user
validates :content, presence: true
scope :for_display, -> { order(:created_at).last(50) }
# Returns a list of users #mentioned in message content.
def mentions
content.scan(/#(#{User::NAME_REGEX})/).flatten.map do |username|
User.find_by(username: username)
end.compact
end
end
room_channel.rb
class RoomChannel < ApplicationCable::Channel
def subscribed
stream_from "room_channel"
stream_from "room_channel_user_#{message_user.id}"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
end
Have to define the username within the create method of messages controller. So the create method in messages_controller.rb looks something like:
def create
message = current_user.messages.build(message_params)
if message.save
ActionCable.server.broadcast 'room_channel',
message: render_message(message)
message.mentions.each do |mention|
ActionCable.server.broadcast "room_channel_user_#{mention.id}",
mention: true,
origin: "##{message.user.username}"
end
end
end
And the room.coffee alert calls data.origin like so:
received: (data) ->
alert("You have a new mention from " + data.origin) if data.mention
if (data.message && !data.message.blank?)
$('#messages-table').append data.message
scroll_bottom()
Thanks to LIUSINING of LearnEnough Society for pointing me in the right direction.
I'm using a Ruby Gem for wit.ai, and have created an API in ROR that queries the wit.ai client.
But unexpectedly the method in the API keeps on calling the client method repeatedly, even after getting the response.
Because of which the API method never renders the JSON response.
How can I resolve this issue? Why is this happening?
It works perfectly fine if I do the same thing from Rails Console.
ApiController
module Api
module V0
class ApiController < ApplicationController
def response
q = params[:q]
response = Api::V0::ApiModel.handle_response q
render :json => response, :status => 200
end
end
end
end
ApiModel
module Api
module V0
class ApiModel
def self.handle_response q
response = ChatbotHelper.query q
if response['type'] == "msg"
message = response["msg"]
json = {"message" => message}
else
json = response
end
json
end
end
end
end
ChatbotHelper
module ChatbotHelper
def self.init
actions = {
send: -> (request, response) {
puts "REQUEST #{request} RESPONSE #{response}"
puts("#{response['text']}")
response
},
getData: -> (context){
},
}
#client = Wit.new(access_token: "XYZ", actions: actions)
end
def self.query q
begin
self.init
response = self.get_response q
rescue SocketError
response = {"message": "SocketError"}
end
response
end
def self.get_response q
puts "GET RESPONSE"
response = #client.converse("b", q, {})
response
end
end
Ahh! it was happening due to the name of the function in the API Controller, i.e. response.. Seems like its an inbuilt function for ROR..
I am new to ruby/rails/spree. I am implementing Indian payment gateway with spree-3.0.7.
I am able to process the order but payment status is always at balance_due.
Controller code
def confirm
payment_method = Spree::PaymentMethod.find(payment_method_id)
Spree::LogEntry.create({
source: payment_method,
details: params.to_yaml
})
order = current_order || raise(ActiveRecord::RecordNotFound)
if(address = order.bill_address || order.ship_address)
firstname = address.firstname
end
#confirm for correct hash and order amount requested before marking an payment as 'complete'
checksum_matched = payment_method.checksum_ok?([params[:status], '', '', '', '', '', '', params[:udf4], params[:udf3], params[:udf2], params[:udf1], order.email, firstname, #productinfo, params[:amount], params[:txnid]], params[:hash])
if !checksum_matched
flash.alert = 'Malicious transaction detected.'
redirect_to checkout_state_path(order.state)
return
end
#check for order amount
if !payment_method.amount_ok?(order.total, params[:amount])
flash.alert = 'Malicious transaction detected. Order amount not matched.'
redirect_to checkout_state_path(order.state)
return
end
payment = order.payments.create!({
source_type: 'Spree::Gateway::Payumoney',#could be something generated by system
amount: order.total,
payment_method: payment_method
})
payment.started_processing!
payment.pend!
order.next
order.update_attributes({:state => "complete", :completed_at => Time.now})
if order.complete?
order.update!
flash.notice = Spree.t(:order_processed_successfully)
redirect_to order_path(order)
return
else
redirect_to checkout_state_path(order.state)
return
end
end
Gateway/Model Code
require "offsite_payments"
module Spree
class Gateway::Payumoney < Gateway
preference :merchant_id, :string
preference :secret_key, :string
def provider_class
::OffsitePayments.integration('Payu_In')
end
def provider
#assign payment mode
OffsitePayments.mode = preferred_test_mode == true ? :test : :production
provider_class
end
def checksum(items)
provider_class.checksum(preferred_merchant_id, preferred_secret_key, items)
end
def auto_capture?
true
end
def method_type
"payumoney"
end
def support?(source)
true
end
def authorization
self
end
def purchase(amount, source, gateway_options={})
ActiveMerchant::Billing::Response.new(true, "payumoney success")
end
def success?
true
end
def txnid(order)
order.id.to_s + order.number.to_s
end
def service_provider
"payu_paisa"
end
def checksum_ok?(itms, pg_hash)
Digest::SHA512.hexdigest([preferred_secret_key, *itms, preferred_merchant_id].join("|")) == pg_hash
end
def amount_ok?(order_total, pg_amount)
BigDecimal.new(pg_amount) == order_total
end
end
in spree payment doc https://guides.spreecommerce.com/developer/payments.html they have mentioned if auto_capture? return true then purchase method will be called but purchase method is not getting called.
Can anyone point me to right direction?
You need not call the following commands
payment.started_processing!
payment.pend!
Just leave the payment in its initial state. i.e. checkout state and complete your order.
Because when order is completed process_payments! is called.
This method processes unprocessed payments whose criteria is like below
def unprocessed_payments
payments.select { |payment| payment.checkout? }
end
Hope this solves your case :)
I fixed the issue by marking payment as complete.
remove
payment.started_processing!
payment.pend!
add
payment.complete
above
order.next
I have published my code at github as gem
https://github.com/isantoshsingh/spree_payumoney