UNPKG

paradigm-core

Version:
279 lines (215 loc) 6.73 kB
const { r, RootModel, ApplicationsPlugin: {ApplicationModel} } = require('structure-core') const {DocumentModel} = require('../../documents') const AppleNewsAPI = require('../lib/apple-news-api') /** * AppleNewsArticleModel Class * * @public * @class AppleNewsArticleModel */ module.exports = class AppleNewsArticleModel extends RootModel { /** * AppleNewsArticleModel constructor * * @public * @constructor * @param {Object} options - Options */ constructor(options = {}) { super(Object.assign({}, { table: 'apple_news_articles', relations: { belongsTo: [ { node: 'documents', link: { foreignKey: 'documentId', localKey: 'appleNewsArticleId' }, joinTable: 'link_apple_news_articles_documents' }, ] } }, options)) } /** * Get or create an apple news client, configured with the channel for the * current application * * @private * @returns {Object} client - apple news 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 AppleNewsAPI({ channelId: app.appleNewsChannelId, apiId: app.appleNewsApiId, apiSecret: app.appleNewsApiSecret }) this.client = client return client } /** * Get an apple news article by document ID * * * @private * @param {String} documentId - the document ID to fetch the apple news * article of * @returns {Object} apple news article document */ async getByDocumentId(documentId) { let res = await r .table('link_apple_news_articles_documents') .getAll(documentId, {index: 'documentId'}) if (res.length === 0) { return } return await this.getById(res[0].appleNewsArticleId) } /** * Create or update an apple news article for a document. * * If an article already exists for the document, update it on Apple News and * update the corresponding local apple news article document. * If an article does not exist yet, create it on APple News and create a * corresponding local apple news article document * * @public * @param {String} documentId - the document ID to create or update for * @returns {Object} the created or updated apple news article document */ async createOrUpdateForDocument(documentId) { try { const client = await this.getClient() const appleNewsArticle = await this.getByDocumentId(documentId) const documentModel = new DocumentModel({ organizationId: this.organizationId, applicationId: this.applicationId, logger: this.logger, }) const articleJson = await documentModel.getAppleNewsFormat(documentId) if (appleNewsArticle) { let article try { article = await client.updateArticle( appleNewsArticle.articleId, appleNewsArticle.articleRevision, articleJson.article, articleJson.bundlesToUrls ) } catch(e) { // If we receive a WRONG_REVISION it's either because we have already // updated the article or because our articleRevsion has become out // of sync. Lets sync the article revision to ensure it is up to date. if (e.apiError && e.apiError.code && e.apiError.code === 'WRONG_REVISION') { this.logger.debug('Apple News Article has already been updated') await this.syncArticleRevsion(appleNewsArticle) return } else { throw e } } this.logger.debug('Successfully updated apple news article', { documentId, articleId: article.articleId, articleRevision: article.revision }) return await RootModel.prototype.updateById.call( this, appleNewsArticle.id, { articleJson, articleRevision: article.revision } ) } else { const article = await client.createArticle( articleJson.article, articleJson.bundlesToUrls ) this.logger.debug('Successfully created apple news article', { documentId, articleId: article.id, articleRevision: article.revision }) return await RootModel.prototype.create.call( this, { documentId, articleJson, articleId: article.id, articleRevision: article.revision } ) } } catch(e) { this.logger.error('Failed to createOrUpdateForDocument', {e}) throw e } } /** * Sync article revision for an apple news article of a document * * * @private * @param {String} appleNewsArticle - the apple news article to sync article revsion of * @returns {Object} synced apple news article document */ async syncArticleRevsion(appleNewsArticle) { const client = await this.getClient() const article = await client.readArticle(appleNewsArticle.articleId) return this.updateById( appleNewsArticle.id, {articleRevision: article.revision} ) } /** * Delete an apple news article for a document. * * Delete the apple news article from Apple News and the corresponding local * apple news article document. * * @public * @param {String} documentId - the document ID to create or update for */ async deleteForDocument(documentId) { try { const client = await this.getClient() const appleNewsArticle = await this.getByDocumentId(documentId) if (!appleNewsArticle) { return } try { await client.deleteArticle( appleNewsArticle.articleId ) } catch(e) { // The article may already have been deleted, in which case just ignore // and continue if (e.apiError && e.apiError.code && e.apiError.code === 'NOT_FOUND') { this.logger.debug('Apple News Article has already been deleted') } else { throw e } } this.logger.debug('Successfully deleted apple news article', { documentId, articleId: appleNewsArticle.articleId }) await RootModel.prototype.deleteById.call(this, appleNewsArticle.id) } catch(e) { this.logger.error('Failed to deleteForDocument', {e}) throw e } } }