Skip to content

Instantly share code, notes, and snippets.

@bibendi
Last active April 2, 2018 16:11
Show Gist options
  • Save bibendi/37e2898c274eb581a4f2260f0716ef93 to your computer and use it in GitHub Desktop.
Save bibendi/37e2898c274eb581a4f2260f0716ef93 to your computer and use it in GitHub Desktop.
# ===== Common side in gem apress-api
Rails.application.routes.draw do
scope module: "apress", constraints: {domain: :current} do
namespace "api/v1" do
post 'callbacks/:service' => 'callbacks#create'
end
end
end
Rails.application.config.api = {
callbacks: ActiveSupport::HashWithIndifferentAccess.new,
events: ActiveSupport::HashWithIndifferentAccess.new([])
}
module Apress
module Api
class CallbacksController < ::Apress::Api::BaseController
def create
service = params.require(:service)
event = params.require(:event)
job = ::Rails.application.config.api[:callbacks].fetch(service).fetch(event).camelize.constantize
event_params = params[:params] || {}
if job.respond_to?(:enqueue)
job.enqueue(event_params)
else
::Resque.enqueue(job, event_params)
end
head 201
end
end
end
end
module Apress
module Api
class DelayedFireCallback
include Interactor
def call
services = Rails.application.config.api[:events].fetch(context.event)
::Resque.redis.multi do
services.each do |service|
::Resque.enqueue(
::Apress::Api::FireCallbackJob,
service,
context.event,
context.params
)
end
end
end
end
end
end
module Apress
module Api
class FireCallbackJob
include Resque::Integration
queue :api_callbacks
retries
def self.perform(serivce, event, params)
callback_class = "#{service}_client/fire_callback".camelize.constantize
callback_class.call!(
event: event,
params: params
)
end
end
end
end
module Apress
module Api
module Callbacks
def notify_service(event:, params: {}, when:)
# Metaprogramming magic
end
end
end
end
# ===== Project side
# apress-companies
module Apress
module Companies
class CompanyUser
extend ::Apress::Api::Callbacks
notify_services event: 'company_user:delete',
params: [:company_id, :user],
when: [:after_commit, on: :destroy]
# !!!! Will be generated as... !!!!
# BEGIN
after_commit(on: :destroy) do
::Apress::Api::DelayedFireCallback.call!(
event: 'company_user:delete',
params: {company_id: company_id, user_id: user}
)
end
# END
end
end
end
# cosmos_client
Rails.application.config.api[:events]['company_user:delete'] << :cosmos
module CosmosClient
class FireCallback
include Interactor
def call
# Если у нас стоит флаг о том, что сервис не готов принимать запросы, то мы не будем этого делать, так как нет гарантий,
# что принятый на той стороне запрос корректно обработается. Мы лучше попробуем его послать позднее,
# выбросив здесь исключение, чтобы отработал механиз retry в FireCallbackJob
context.fail!(message: 'cosmos.updates_locked') if ::Apress::Orders.updates_locked?
callback = ::CosmosClient::Callback.new(event: context.event, params: context.params)
context.callback = callback
context.fail!(message: 'cosmos.fire_callback.failure') unless callback.save
end
end
end
module CosmosClient
class Callback < CosmosClient::Base
self.site = ::CosmosClient.api_url + "/callbacks/cosmos"
end
end
# ===== Cosmos (Apress::Orders at now) side
Rails.application.config.api[:callbacks][:cosmos] = {
'company_user:delete' => 'apress/orders/reset_manager_to_default_job'
}
# https://github.com/abak-press/apress-orders/blob/master/app/jobs/apress/orders/reset_manager_to_default_job.rb#L4
# FIXME: Убрать уникальность, которая к тому же сделана и с ошибкой
module Apress
module Orders
class ResetManagerToDefaultJob
include Resque::Integration
queue :orders_unbind_manager
retries
def self.perform(params)
# ...
end
end
end
end
@deniskorobicyn
Copy link

deniskorobicyn commented Apr 2, 2018

А тут - https://gist.github.com/bibendi/37e2898c274eb581a4f2260f0716ef93#file-service-callbacks-rb-L19 - случаем DoS не возможен?
Я так понимаю тут надо какие-то ограничения поставить на того, кто может отправлять туда запрос?
Например, что это может сделать только проект в котором задан какой-нибудь secret-token общий с сервисным проектом.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment