Instagram Graph API
Specs
https://docs.google.com/document/d/1hdt7MpnNAfQjzvLoYfooLiMxrt1QnSiWX6MR02HbfG4/edit
DB tables structure
There 3 tables
With following structure
Table “Instagram_installs” - stores tokens and corresponding instagram account data.
| Column | Description |
|---|---|
| instagram_account | Stores instagram account name string, e.g. “trees” for account https://www.instagram.com/trees/ |
| instagram_business_account_id | Insta account id connected to user selected FB page |
| first_verify_email_sent_at | Tracks the datetime of the first email which is sent when (currently) 10 days are left before token expiration. Note: this field is reset to when the user reconnects instagram account. |
| second_verify_email_sent_at | Tracks the datetime of the second which is sent when the token already expired. Note: this field is reset when the user reconnects instagram account. |
Table “Instagram_apps” - Stores 1-to-Many connections between instagram tokens and apps. Multiple apps will use the same token if they connect to the same instagram account.
Table “Instagram_hashtags” - Acts as a lookup cache to obtain Instagram Graph API “hashtag_id” by hashtag name to preserve API limits.
E.g. when user searches hashtag “food”, and there's no corresponding data in this table then API request is made to Graph API and the result “hashtag_id” is stored in table
Obtaining token flow
Users connect instagram accounts by selecting the connected FB page.
Then an XHR request is made to “instagram#oauth” to obtain a long term token. E.g. request https://www.powr.io/instagram/oauth?token=...&appId=28049484&user=3263780&app_type=socialFeed contains “token” which is short token (expiration ~2 hours) and which server exchanges with long token (expiration 90 days) using FB Graph API endpoint:
If all scopes and connected FB page are correct, then “instagram#oauth” responses
Then immediately client makes request to “instagram#index” to get instagram data, e.g.
https://www.powr.io/instagram?app_id=28049484&following=powr_io&page_name=powr_io&username_or_hashtag=@&instagram_username=powr_io&is_settings=true. Which returns data in following structure
On client side the server response data is handled by
getResultsFromPowrServer() function (/powr/app/javascript/helpers/socialfeed/instagram.js)
Sending token refresh emails
If token is close to expire or already expired then emails are sent by “**codeinstagram_graph_api_refresh_tokenscode” rake task in “/powr/lib/tasks/scheduler.rake” file which is run (currently) once a day:
Action button in the email contains an oauth link of the form **"https://www.facebook.com/v9.0/dialog/oauth?client_id=#{client_id}&redirect_uri=#{redirect_uri}&scope=instagram_basic,pages_read_engagement"
Where “redirect_uri” is also handled by “instagram#oauth”, but instead of a short token, the code param is sent.
https://www.powr.io/users/oauth/?code=.....
Migration flow for apps that were created previously before Graph API integration
- Migration is applied only for “social feed” and it’s aliases
- Rollout “instagram_graph_api_migrate_apps” controls user accounts which will see migration flow
- For apps that are 1) not new (i.e. app.backup_content present) and 2) were saved Graph API enabled, then attribute APP_MODEL.attributes.instagram_graph_api_migrated_at is set to “never”.
- APP_MODEL.attributes.instagram_graph_api_migrated_at is used to determine if migration was already performed and whether to show upgrade banner.
- After the user Connects instagram account by clicking “Continue with Facebook”, then following items are migrated to Graph API mode 1) all hashtags 2) connected instagram username. Other instagram accounts are still served by non Graph API mode (i.e. from powr-outlet), because Graph API allows fetching data only of connected accounts. In this example only “#food” and “@powr_io” will be migrated.
- Also after successful migration the attributeAPP_MODEL.attributes.instagram_graph_api_migrated_at will be changed from “never” to the current date.
- Not migrated instagram accounts still needs to load correctly, so its required to determine which instagram account should be served from “powr-outlet”, and which from Graph API. To determine this we store the not migrated instagram accounts in APP_MODEL.atreibutes.instagramNotMigratedFeeds array. (@natgeo, @apple, @trees in this case). And once a user Connects one of these accounts to Graph API, then this account is removed from APP_MODEL.atreibutes.instagramNotMigratedFeeds.