Module: FlowChat::GatewayAsyncSupport

Overview

Concern for gateways to support async background processing Mix this into gateway classes to enable async detection and job enqueueing

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context.



7
8
9
# File 'lib/flow_chat/gateway_async_support.rb', line 7

def context
  @context
end

#controllerObject (readonly)

Returns the value of attribute controller.



7
8
9
# File 'lib/flow_chat/gateway_async_support.rb', line 7

def controller
  @controller
end

Instance Method Details

#async_supported?Boolean

Check if gateway supports async processing Override in gateways that don't support async (e.g., USSD)

Returns:

  • (Boolean)


11
12
13
# File 'lib/flow_chat/gateway_async_support.rb', line 11

def async_supported?
  true
end

#enqueue_async_jobObject

Enqueue background job with serialized request context Returns true if job was enqueued, false otherwise



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/flow_chat/gateway_async_support.rb', line 47

def enqueue_async_job
  return false unless should_enqueue_async?

  processor = @context["processor"]

  FlowChat.logger.info { "#{self.class.name}: Async enabled - enqueuing background job" }

  # Serialize request data for BackgroundController
  #
  # side_events_published records that this pass has already announced the
  # delivery's side events, so the job it is about to enqueue does not
  # announce them a second time. It rides in the request context rather
  # than in params, where a platform's own webhook fields live.
  request_data = {
    params: @controller.request.params.to_h,
    method: @controller.request.method,
    headers: extract_headers_for_background(@controller.request),
    host: extract_host(@controller.request),
    path: extract_path(@controller.request),
    body: extract_body_for_background(@controller.request),
    remote_ip: extract_remote_ip(@controller.request),
    side_events_published: true
  }

  # Enqueue user's job with request context and job params
  processor.async_job_class.perform_later(
    request_context: request_data,
    **processor.async_job_params
  )

  FlowChat.logger.info { "#{self.class.name}: Background job enqueued successfully" }

  true
end

#extract_body_for_background(request) ⇒ Object

Extract request body for background processing Override in gateways that need the request body



107
108
109
110
111
112
113
114
115
# File 'lib/flow_chat/gateway_async_support.rb', line 107

def extract_body_for_background(request)
  return nil unless request.body

  body_content = request.body.read
  request.body.rewind  # Reset for subsequent reads
  body_content
rescue
  nil
end

#extract_headers_for_background(request) ⇒ Object

Extract serializable headers needed for background processing Override in gateways that need additional headers



84
85
86
87
88
89
# File 'lib/flow_chat/gateway_async_support.rb', line 84

def extract_headers_for_background(request)
  {
    "Content-Type" => request.headers["Content-Type"],
    "User-Agent" => request.headers["User-Agent"]
  }.compact
end

#extract_host(request) ⇒ Object

Extract host from request for URL boundary support



92
93
94
95
96
# File 'lib/flow_chat/gateway_async_support.rb', line 92

def extract_host(request)
  request.host
rescue
  nil
end

#extract_path(request) ⇒ Object

Extract path from request for URL boundary support



99
100
101
102
103
# File 'lib/flow_chat/gateway_async_support.rb', line 99

def extract_path(request)
  request.path
rescue
  nil
end

#extract_remote_ip(request) ⇒ Object

Extract remote IP from request



118
119
120
121
122
# File 'lib/flow_chat/gateway_async_support.rb', line 118

def extract_remote_ip(request)
  request.remote_ip
rescue
  nil
end

#in_background?Boolean

Detect if we're currently in background mode

Returns:

  • (Boolean)


16
17
18
# File 'lib/flow_chat/gateway_async_support.rb', line 16

def in_background?
  @controller.is_a?(::FlowChat::BackgroundController)
end

#should_enqueue_async?Boolean

Check if async processing should be used Returns true if:

  • Not already in background mode
  • Processor has async enabled
  • Gateway supports async

Returns:

  • (Boolean)


37
38
39
40
41
42
43
# File 'lib/flow_chat/gateway_async_support.rb', line 37

def should_enqueue_async?
  processor = @context["processor"]

  !in_background? &&
    processor&.async_enabled? &&
    async_supported?
end

#side_events_already_published?Boolean

Whether an earlier pass already announced this delivery's side events, which is the case only for a job the gem enqueued after publishing them itself. Being in the background is not enough on its own: an application may build a request context and enqueue a job with no foreground pass ahead of it, and then this pass is the first to see the delivery.

in_background? short-circuits because a foreground controller is a Rails controller, which knows nothing about side events.

Returns:

  • (Boolean)


28
29
30
# File 'lib/flow_chat/gateway_async_support.rb', line 28

def side_events_already_published?
  in_background? && @controller.side_events_published?
end