Class: Aoa::WebhookWorker

Inherits:
ApplicationWorker
  • Object
show all
Defined in:
app/workers/aoa/webhook_worker.rb

Constant Summary collapse

ROUTE =
'partner_payment_notification'.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#reservationObject (readonly)

Returns the value of attribute reservation.



2
3
4
# File 'app/workers/aoa/webhook_worker.rb', line 2

def reservation
  @reservation
end

#restaurantObject (readonly)

Returns the value of attribute restaurant.



2
3
4
# File 'app/workers/aoa/webhook_worker.rb', line 2

def restaurant
  @restaurant
end

Class Method Details

.default_perform_async(reservation_id, status) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'app/workers/aoa/webhook_worker.rb', line 9

def self.default_perform_async(reservation_id, status)
  BUSINESS_LOGGER.set_business_context({ reservation_id: reservation_id,
                                         vendor_name: ApiVendorV1::Constants::AOA_VENDOR_NAME })
  BUSINESS_LOGGER.info("#{self.class}: default_perform_async method called with reservation_id: #{reservation_id}, status: #{status}")

  # we need to use delay to make sure that the reservation is already saved
  # before sending the webhook

  delay = AdminSetting.aoa_notification_delay.to_i.seconds
  reservation = Reservation.fetch(reservation_id)

  if reservation.blank?
    error = "#{self.class}: Failed to send AOA notification because Reservation not found. Reservation ID: #{reservation_id}"
    BUSINESS_LOGGER.error(error)
    APMErrorHandler.report(error)
    return
  end

  if reservation.booking_channel.aoa?
    # we need to use delay to make sure that the reservation is already saved
    BUSINESS_LOGGER.info("#{self.class}: Worker scheduled to be run in #{delay} seconds with status: #{status}")
    Aoa::WebhookWorker.perform_in(delay, reservation_id, status)

    # we also need to execute the webhook, 24 hours after the dining time
    # to make sure that AOA system has the correct status
    # because sometimes restaurant staff forget to mark the reservation as arrived
    perform_at = reservation.reservation_time.twenty_four_hours_later + delay
    BUSINESS_LOGGER.info("#{self.class}: Worker scheduled to be run at #{perform_at} with status: :arrived")
    Aoa::WebhookWorker.perform_at(perform_at, reservation_id, :arrived)
  else
    error = "#{self.class}: Failed to send AOA notification because this is not AOA Reservation. Reservation ID: #{reservation_id}"
    BUSINESS_LOGGER.error(error)
    APMErrorHandler.report(error)
  end
end

Instance Method Details

#perform(reservation_id, status) ⇒ Object



45
46
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'app/workers/aoa/webhook_worker.rb', line 45

def perform(reservation_id, status)
  BUSINESS_LOGGER.set_business_context({ reservation_id: reservation_id,
                                         vendor_name: ApiVendorV1::Constants::AOA_VENDOR_NAME })
  BUSINESS_LOGGER.info("#{self.class}: perform method called with reservation_id: #{reservation_id}, status: #{status}")

  @reservation = Reservation.fetch(reservation_id)&.decorate

  if reservation.blank?
    error = "#{self.class}: Failed to send AOA notification because Reservation not found. Reservation ID: #{reservation_id}"
    BUSINESS_LOGGER.error(error)
    APMErrorHandler.report(error)
    return
  end

  @restaurant = reservation.restaurant

  RequestStore.store[:reservation_id] ||= reservation.id
  RequestStore.store[:restaurant_id] ||= restaurant.id

  if !reservation.booking_channel.aoa?
    error = "#{self.class}: Failed to send AOA notification because this is not AOA Reservation. Reservation ID: #{reservation_id}"
    BUSINESS_LOGGER.error(error)
    APMErrorHandler.report(error)
    return
  end

  if reservation.aoa_reservation&.persisted?
    BUSINESS_LOGGER.info("#{self.class}: aoa_reservation is already persisted")
  else
    BUSINESS_LOGGER.info("#{self.class}: aoa_reservation is NOT persisted")
    old_reservation = Reservation.find_by(id: reservation.old_reservation_id)
    if old_reservation.blank?
      error = "#{self.class}: Failed to send AOA notification because old reservation is not found. Reservation ID: #{reservation.id}"
      BUSINESS_LOGGER.error(error)
      APMErrorHandler.report(error)
      return
    end
    old_aoa_reservation = old_reservation.aoa_reservation
    old_aoa_reservation.reservation_id = reservation.id
    old_aoa_reservation.save!
    reservation.aoa_reservation = old_aoa_reservation
    BUSINESS_LOGGER.info("#{self.class}: Updated AOA reservation (ID: #{old_aoa_reservation.id}) to
      associate with new reservation (ID: #{reservation.id}).")
  end

  current_restaurant_time = Time.use_zone restaurant.time_zone do
    Time.zone.now
  end

  case status.to_sym
  when :arrived
    # We assume that the reservation status as ARRIVED
    # if restaurant didn't update the reservation status to arrive, after 24 hours
    # from dining time
    if current_restaurant_time >= reservation.reservation_time.twenty_four_hours_later
      return send_arrived
    end

    wait_until_reservation_status_has_changed(reservation_id, :arrived) do
      send_arrived
    end
  when :pending_arrival
    # We assume that the reservation status as ARRIVED
    # if restaurant didn't update the reservation status to arrive, after 24 hours
    # from dining time
    if current_restaurant_time >= reservation.reservation_time.twenty_four_hours_later
      return send_arrived
    end

    wait_until_reservation_status_has_changed(reservation_id, :pending_arrival) do
      send_pending_arrival
    end
  else
    raise NotImplementedError
  end
end

#send_arrivedObject



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'app/workers/aoa/webhook_worker.rb', line 211

def send_arrived
  reservation.with_lock do
    note = reservation.aoa_reservation.note.presence || ''
    if reservation.aoa_reservation.webhook_arrived_sent_at.present?
      new_note = "Skip sending AOA notification for arrived because it has been sent. Time: #{Time.zone.now}"
      BUSINESS_LOGGER.info("#{self.class}: #{new_note}")
      note += new_note
      reservation.aoa_reservation.update! note: note
      return
    end

    if reservation.aoa_reservation.webhook_pending_arrival_sent_at.present? # return false if pending arrival has been sent (prepaid)
      new_note = "Skip sending AOA notification for arrived because pending arrival has been sent. Time: #{Time.zone.now}"
      BUSINESS_LOGGER.info("#{self.class}: #{new_note}")
      note += new_note
      reservation.aoa_reservation.update! note: note
      return
    end

    if reservation.no_show? # retun false if no show for prepaid or not
      new_note = "Skip sending AOA notification for arrived because the reservation is no show. Time: #{Time.zone.now}"
      BUSINESS_LOGGER.info("#{self.class}: #{new_note}")
      note += new_note
      reservation.aoa_reservation.update! note: note
      return
    end

    if old_booking_has_sent_notif?
      new_note = "Skip sending AOA notification for arrived because the old booking has been sent. Time: #{Time.zone.now}"
      BUSINESS_LOGGER.info("#{self.class}: #{new_note}")
      note += new_note
      reservation.aoa_reservation.update! note: note
      return
    end

    response = send_webhook

    if response.code.to_i != 204
      error = "#{self.class}: AOA Notif api error"
      error_message = 'Failed to send AOA notification for arrived.'
      error_info = { response_code: response.code.to_i, response_body: response.body,
                     error_message: error_message, reservation_id: reservation.id }

      new_note = "#{error_message} Error: #{response.body}. Time: #{Time.zone.now}"
      VendorLogger.log_event(:webhook_out, ROUTE, payload: error_info, custom_message: :error)
      APMErrorHandler.report(error, error_info: error_info, new_note: new_note)
      note += new_note
      reservation.aoa_reservation.update! note: note
    else
      time = Time.zone.now
      BUSINESS_LOGGER.info("#{self.class}: webhook_arrived_sent_at: #{time}")
      reservation.aoa_reservation.update! webhook_arrived_sent_at: time
    end
  end
end

#send_pending_arrivalObject



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'app/workers/aoa/webhook_worker.rb', line 164

def send_pending_arrival
  reservation.with_lock do
    note = reservation.aoa_reservation.note.presence || ''
    if reservation.aoa_reservation.webhook_pending_arrival_sent_at.present?
      new_note = "Skip sending AOA notification for pending arrival because it has been sent. Time: #{Time.zone.now}"
      BUSINESS_LOGGER.info("#{self.class}: #{new_note}")
      note += new_note
      reservation.aoa_reservation.update! note: note
      return
    end

    if old_booking_has_sent_notif?
      new_note = "Skip sending AOA notification for pending arrival because the old booking has been sent. Time: #{Time.zone.now}"
      BUSINESS_LOGGER.info("#{self.class}: #{new_note}")
      note += new_note
      reservation.aoa_reservation.update! note: note
      return
    end

    if prepaid?
      BUSINESS_LOGGER.info("#{self.class}: Reservation is prepaid, sending AOA notification for pending arrival")
      response = send_webhook

      if response.code.to_i != 204
        error = "#{self.class}: AOA Notif api error"
        error_message = 'Failed to send AOA notification for pending arrival.'
        new_note = "#{error_message} Error: #{response.body}. Time: #{Time.zone.now}"
        error_info = { response_code: response.code.to_i, response_body: response.body,
                       reservation_id: reservation.id, error_message: error_message }
        VendorLogger.log_event(:webhook_out, ROUTE, payload: error_info, custom_message: :error)
        APMErrorHandler.report(error, error_info: error_info)
        note += new_note
        reservation.aoa_reservation.update! note: note
      else
        time = Time.zone.now
        BUSINESS_LOGGER.info("#{self.class}: webhook_pending_arrival_sent_at: #{time}")
        reservation.aoa_reservation.update! webhook_pending_arrival_sent_at: time
      end
    else
      new_note = 'Skip sending AOA notification for pending arrival because the reservation is not prepaid.'
      BUSINESS_LOGGER.info("#{self.class}: #{new_note}")
      note += new_note
      reservation.aoa_reservation.update! note: note
    end
  end
end

#wait_until_reservation_status_has_changed(reservation_id, status) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'app/workers/aoa/webhook_worker.rb', line 122

def wait_until_reservation_status_has_changed(reservation_id, status)
  reservation = Reservation.fetch(reservation_id)

  if reservation.aoa_reservation&.persisted?
    BUSINESS_LOGGER.info("#{self.class}: aoa_reservation is already persisted")
  else
    BUSINESS_LOGGER.error("#{self.class}: Failed to send AOA notification because aoa_reservation is not persisted")
    return
  end

  if reservation.status_as_symbol == :cancelled
    BUSINESS_LOGGER.error("#{self.class}: Skip sending AOA notification because the reservation is cancelled")
    return
  end

  retry_times = 0
  loop do
    reservation = Reservation.fetch(reservation_id)
    if reservation.status_as_symbol != status

      # if the reservation status is already arrived, we can skip the waiting
      if reservation.status_as_symbol == :arrived && status == :pending_arrival
        yield

        break
      end

      retry_times += 1
      if retry_times == 100
        BUSINESS_LOGGER.error("#{self.class}: Unknown reservation status")
        raise 'Unknown reservation status'
      end

      sleep 1
    else
      yield

      break
    end
  end
end