Class: InstagramConnect::AccountReadinessJob

Inherits:
ApplicationJob
  • Object
show all
Defined in:
app/jobs/instagram_connect/account_readiness_job.rb

Overview

Closes the gap between "an operator clicked Connect" and "events actually flow", then keeps it closed.

Two things stand in that gap and neither has a dashboard equivalent:

1. TOKEN GRADE. The OAuth exchange returns a long-lived *user* token, but
 every Instagram messaging call and the subscription below are Page
 token operations. This swaps in the Page token. Page tokens minted from
 a long-lived user token do not expire, so the expiry is cleared with it.
2. PAGE SUBSCRIPTION. Ticking webhook fields in the app dashboard
 subscribes the *app*. Meta delivers nothing until the *Page* lists the
 app in its subscribed_apps, which is a per-Page Graph call.

Both steps read before they write, so a scheduled run costs two GETs and changes nothing once an account is healthy. That is the point: a token Meta rotates, or a subscription dropped when an app moves between Development and Live mode, heals itself rather than becoming a console command nobody remembers to run.

This lives in the gem rather than in a host because the field list has to track what Ingest can actually parse. Maintained separately, the two drift and the host silently receives events it drops.

Constant Summary collapse

HTTP_TIMEOUT =
15

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.connectableObject



54
55
56
# File 'app/jobs/instagram_connect/account_readiness_job.rb', line 54

def self.connectable
  Account.active.where.not(page_id: [ nil, "" ])
end

.enqueue_if_neededObject

Cheap negative check so an install with no connected account never enqueues anything.



46
47
48
49
50
51
52
# File 'app/jobs/instagram_connect/account_readiness_job.rb', line 46

def self.enqueue_if_needed
  return :nothing_to_do if InstagramConnect.configuration.app_id.blank?
  return :nothing_to_do unless connectable.exists?

  perform_later
  :enqueued
end

.page_subscribed_fieldsObject

What subscribed_apps will actually accept — the Page object names, minus the fields that only exist on the instagram object. Sending an Instagram name here fails the ENTIRE call, so the two lists are deliberately separate rather than one list used twice.



40
41
42
# File 'app/jobs/instagram_connect/account_readiness_job.rb', line 40

def self.page_subscribed_fields
  Ingest::Registry.page_subscribable_fields
end

.subscribed_fieldsObject

What we ask Meta to deliver to the webhook endpoint.



32
33
34
# File 'app/jobs/instagram_connect/account_readiness_job.rb', line 32

def self.subscribed_fields
  Ingest::Registry.subscribable_fields
end

Instance Method Details

#performObject



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'app/jobs/instagram_connect/account_readiness_job.rb', line 58

def perform
  return if InstagramConnect.configuration.app_id.blank?

  self.class.connectable.find_each do ||
    # Cleared up front rather than on success, so a step that records a soft
    # failure (no Page token, say) is not immediately wiped by the same pass
    # completing without raising.
    .update_columns(readiness_error: nil) if .readiness_error.present?

    ensure_page_token()
    ensure_subscription()
  rescue StandardError => e
    # One unhealthy account must not stop the rest, and the next run retries
    # anyway. The reason is kept on the row so a health screen can show it.
    .update_columns(readiness_error: "#{e.class}: #{e.message}".truncate(255))
    logger&.error("[InstagramConnect] readiness failed for account=#{.id}: #{e.message}")
  end
end