paradigm-facebook-ia
Version:
Paradigm Facebook Instant Articles
262 lines (202 loc) • 6.5 kB
JavaScript
const r = require('structure-driver')
const RootModel = require('structure-root-model')
const {DocumentModel} = require('paradigm-documents')
const {ApplicationModel} = require('structure-applications')
const CheckImportJob = require('../jobs/check-import')
const FacebookIAAPI = require('../lib/facebook-ia-api')
/**
* FacebookInstantArticleModel Class
*
* @public
* @class FacebookInstantArticleModel
*/
class FacebookInstantArticleModel extends RootModel {
/**
* FacebookInstantArticleModel constructor
*
* @public
* @constructor
* @param {Object} options - Options
*/
constructor(options = {}) {
super(Object.assign({}, {
table: 'facebook_instant_articles',
relations: {
belongsTo: [
{
node: 'documents',
link: {
foreignKey: 'documentId',
localKey: 'facebookInstantArticleId'
},
joinTable: 'link_facebook_instant_articles_documents'
},
]
}
}, options))
}
/**
* Get or create a Facebook API client, configured with the page and access
* token for the current application
*
* @private
* @returns {Object} client - Facebook API client instance
*/
async getClient() {
if (this.client) {
return this.client
}
const applicationModel = new ApplicationModel({
organizationId: this.organizationId,
logger: this.logger,
})
const app = await applicationModel.getById(this.applicationId)
const client = new FacebookIAAPI(
app.facebook.pageId,
app.facebook.pageAccessToken
)
this.client = client
return client
}
/**
* Get a facebook instant article by document ID
*
*
* @private
* @param {String} documentId - the document ID to fetch the fb instant
* article of
* @returns {Object} facebook instant article document
*/
async getByDocumentId(documentId) {
let res = await r
.table('link_facebook_instant_articles_documents')
.getAll(documentId, {index: 'documentId'})
if (res.length === 0) {
return
}
return await this.getById(res[0].facebookInstantArticleId)
}
/**
* Create or update an facebook instant article for a document.
*
* If an article already exists for the document, update it on Facebook and
* update the corresponding local facebook instant article document.
* If an article does not exist yet, create it on Facebook and create a
* corresponding local facebook instant article document
*
* @public
* @param {String} documentId - the document ID to create or update for
* @returns {Object} the created or updated facebook instant article document
*/
async createOrUpdateForDocument(documentId) {
try {
const client = await this.getClient()
let fbia = await this.getByDocumentId(documentId)
const documentModel = new DocumentModel({
organizationId: this.organizationId,
applicationId: this.applicationId,
logger: this.logger,
})
const checkImportJob = new CheckImportJob({
organizationId: this.organizationId,
applicationId: this.applicationId,
logger: this.logger
})
const {markup, canonicalUrl} = await documentModel.getFacebookIAFormat(
documentId
)
if (fbia) {
// If the article hasn't actually changed, just return
if (fbia.canonicalUrl === canonicalUrl && fbia.markup === markup) {
return fbia
}
// The article has not finished importing. Throw an error so that the
// job retries.
if (fbia.importStatus === 'importing') {
throw `Article not finished importing, can't update yet`
}
// If the canonical URL has changed, delete the article for the old URL
if (fbia.canonicalUrl !== canonicalUrl && fbia.importStatus === 'success') {
client.deleteArticle(fbia.articleId)
}
const article = await client.createArticle(markup)
this.logger.debug('Successfully updated facebook instant article', {
documentId,
canonicalUrl,
markup,
articleImportId: article.id,
})
fbia = await RootModel.prototype.updateById.call(
this,
fbia.id,
{
markup,
canonicalUrl,
articleImportId: article.id,
articleId: null,
importStatus: 'importing',
}
)
} else {
const article = await client.createArticle(markup)
this.logger.debug('Successfully created facebook instant article', {
documentId,
articleImportId: article.id,
canonicalUrl
})
fbia = await RootModel.prototype.create.call(
this,
{
documentId,
markup,
canonicalUrl,
articleImportId: article.id,
importStatus: 'importing',
}
)
}
const now = new Date()
var oneMinute = new Date(now.getTime() + (60 * 1000))
checkImportJob.queue({fbia}, {delayDate: oneMinute})
return fbia
} catch(e) {
this.logger.error('Failed to createOrUpdateForDocument', {e})
throw e
}
}
/**
* Delete an facebook instant article for a document.
*
* Delete the facebook instant article from Facebook and the corresponding
* local facebook instant article document.
*
* @public
* @param {String} documentId - the document ID to create or update for
*/
async deleteForDocument(documentId) {
try {
const client = await this.getClient()
const fbia = await this.getByDocumentId(documentId)
if (!fbia) {
return
}
// The article has not finished importing yet. Throw an error so that the
// job retries.
if (fbia.importStatus === 'importing') {
throw `Article not finished importing, can't delete yet`
}
if (fbia.importStatus !== 'failed') {
await client.deleteArticle(fbia.articleId)
this.logger.debug('Successfully deleted facebook instant article', {
documentId,
articleId: fbia.articleId
})
}
await RootModel.prototype.deleteById.call(this, fbia.id)
} catch(e) {
this.logger.error('Failed to deleteForDocument', {e})
throw e
}
}
}
module.exports = FacebookInstantArticleModel