paradigm-channels
Version:
608 lines (444 loc) • 15.1 kB
JavaScript
const {DigitalAssetModel} = require('structure-digital-assets')
const paradigmErrorCodes = require('paradigm-error-codes')
const r = require('structure-driver')
const RootModel = require('structure-root-model')
const structureErrorCodes = require('structure-error-codes')
/**
* ChannelModel Class
*
* @public
* @class ChannelModel
*/
class ChannelModel extends RootModel {
/**
* ChannelModel constructor
*
* @public
* @constructor
* @param {Object} options - Options
*/
constructor(options = {}) {
super(Object.assign({}, {
table: 'channels',
relations: {
hasManyAndBelongsTo: [
{
node: 'channels',
link: {
foreignKey: 'channelParentId',
localKey: 'channelChildId'
},
joinTable: 'link_channels_channels'
}
]
}
}, options))
}
getAll(ids = [], options = {}) {
const applicationId = this.applicationId || options.applicationId
return new Promise( async (resolve, reject) => {
try {
let query = r
.table(this.table)
if(ids.length > 0) {
query = query
.getAll(r.args(ids))
} else {
query = query
.getAll(applicationId, {index: 'applicationId'})
.filter(function(doc) {
return doc('status').eq('active')
})
}
const channels = await query.run()
resolve(channels)
} catch(e) {
this.logger.error(e)
reject(e)
}
})
}
getBySlug(slug, options = {}) {
const applicationId = this.applicationId || options.applicationId
return new Promise( async (resolve, reject) => {
try {
const doc = await r
.table(this.table)
.getAll([slug, applicationId], {index: 'link_channelSlug_applicationId'})
if(doc.length > 0) {
return resolve(doc[0])
}
resolve(false)
} catch(e) {
this.logger.error(e)
reject(e)
}
})
}
getAllResources(params = {}, query = {}, options = {}) {
const applicationId = this.applicationId || options.applicationId
const id = params.id
const ignoreDocumentIds = (query.ignoreDocumentIds) ? ((query.ignoreDocumentIds instanceof Array) ? query.ignoreDocumentIds : query.ignoreDocumentIds.split(',')) : []
const organizationId = this.organizationId || options.organizationId
const slug = params.slug
const daModel = new DigitalAssetModel({
applicationId,
logger: this.logger,
organizationId
})
const {DocumentModel} = require('paradigm-documents') // prevent circular dep
const documentModel = new DocumentModel({
applicationId,
logger: this.logger,
organizationId
})
return new Promise( async (resolve, reject) => {
try {
let channel
if(id) {
channel = await this.getById(id)
} else {
channel = await this.getBySlug(slug, {
applicationId,
organizationId
})
}
if(!channel) {
return reject(paradigmErrorCodes('CHANNEL_INVALID', {
applicationId,
id,
organizationId,
slug
}))
}
if(!(channel.resources instanceof Array)) channel.resources = []
let map = new Map()
const categoriesMap = new Map()
const categoryIds2SlugMap = {}
const categoriesSlugsMap = new Map()
const categoriesList = await r
.table('categories')
.getAll(applicationId, {index: 'applicationId'})
for (const category of categoriesList) {
if(category.slug.length > 0) {
categoryIds2SlugMap[category.id] = category.slug
categoriesSlugsMap.set(category.slug, category)
categoriesMap.set(category.id, category)
}
}
for (const resource of channel.resources) {
const categoryIds = []
const categorySlugs = resource.categorySlugs || []
const digitalAssetIds = []
const digitalAssetMap = {}
// Convert slugs to ids
if(categorySlugs.length > 0) {
for (const slug of categorySlugs) {
const category = categoriesSlugsMap.get(slug)
if(category && category.id) {
categoryIds.push(category.id)
}
}
}
const pkg = {
applicationId,
categoryIds,
expand: false,
ignoreDocumentIds,
mode: [],
order: resource.order || {by: 'publishedAt', dir: 'desc'},
status: ['published'],
tags: resource.tags || [],
taxonomyOperator: resource.taxonomyOperator || 'or',
userId: '',
title: '',
}
const order = pkg.order
let query = r
.table('documents')
.getAll(applicationId, {index: 'applicationId'})
.filter(documentModel._getAllDocFilter(pkg))
if (options.rss) {
query = query
.eqJoin('activeRevisionId', r.table('document_revisions'))
.map(function(doc) {
return doc('left').merge({fields: doc('right')('fields')})
})
.eqJoin('userId', r.table('users'))
.map(function(doc) {
return doc('left').merge({user: doc('right')})
})
}
query = query
.orderBy(r[order.dir](order.by))
if (options.rss) {
query = query.limit(30)
} else if(resource.limit) {
query = query.limit(resource.limit)
}
const res = await query.run()
const docs = []
const docMap = new Map()
for(let j = 0, k = res.length; j < k; j++) {
const doc = res[j]
doc.categories = []
ignoreDocumentIds.push(doc.id)
if(doc.categoryIds && doc.categoryIds.length > 0) {
for(let m = 0, n = doc.categoryIds.length; m < n; m++) {
const categoryId = doc.categoryIds[m]
doc.categories.push(categoriesMap.get(categoryId))
}
}
if(doc.featuredImageId) {
digitalAssetIds.push(doc.featuredImageId)
digitalAssetMap[doc.featuredImageId] = doc.id
}
doc.permalink = documentModel.generatePermalink2(doc)
docMap.set(doc.id, doc)
}
if(digitalAssetIds.length > 0) {
const daRes = await daModel.getAll(digitalAssetIds)
for(let i = 0, l = daRes.length; i < l; i++) {
const da = daRes[i]
const docId = digitalAssetMap[da.id]
const tDoc = docMap.get(docId)
tDoc.featuredImage = da
docMap.set(docId, tDoc)
}
}
for(let [key, value] of docMap) {
docs.push(value)
}
map.set(resource.name, docs)
}
resolve({
channel,
resources: Array.from(map)
})
} catch(e) {
reject(e)
}
})
}
/**
* Get channels of a category slug
*
* @public
* @constructor
* @param {Object} options - Options
*/
async ofCategory(categorySlug) {
const applicationId = this.applicationId
const docs = await r
.table('channels')
.filter({
applicationId
})
.filter(function(channel) {
return channel('resources')('categorySlugs')
.concatMap(function(x) {
return x
}) // flatten to return the top-level document
.contains(categorySlug)
})
return docs
}
/**
* Get channels of a document
*
* @public
* @constructor
* @param {Object} options - Options
*/
async ofDocument(documentId) {
const applicationId = this.applicationId
const organizationId = this.organizationId
const {DocumentModel} = require('paradigm-documents') // prevent circular dep
const documentModel = new DocumentModel({
organizationId,
applicationId,
logger: this.logger
})
const doc = await documentModel.getById(documentId)
const categorySlugs = []
for (const category of doc.categories) {
if (!(categorySlugs.includes(category.slug))) {
categorySlugs.push(category.slug)
}
}
const channels = []
for (const categorySlug of categorySlugs) {
const categoryChannels = await this.ofCategory(categorySlug)
for (const channel of categoryChannels) {
if (channels.some((x) => { return x.id === channel.id })) {
continue
}
channels.push(channel)
}
}
return channels
}
getSingleResource(params = {}, query = {}, options = {}) {
const applicationId = this.applicationId || options.applicationId
const id = params.id
const ignoreDocumentIds = (query.ignoreDocumentIds) ? ((query.ignoreDocumentIds instanceof Array) ? query.ignoreDocumentIds : query.ignoreDocumentIds.split(',')) : []
const organizationId = this.organizationId || options.organizationId
const slug = params.slug
const selectedResource = params.resource || false
const page = (query.page) ? parseInt(query.page, 10) - 1 : 0
const daModel = new DigitalAssetModel({
applicationId,
logger: this.logger,
organizationId
})
const {DocumentModel} = require('paradigm-documents') // prevent circular dep
const documentModel = new DocumentModel({
applicationId,
logger: this.logger,
organizationId
})
return new Promise( async (resolve, reject) => {
try {
let channel
if(id) {
channel = await this.getById(id)
} else {
channel = await this.getBySlug(slug, {
applicationId,
organizationId
})
}
if(!channel) {
return reject(structureErrorCodes('CHANNEL_INVALID', {
applicationId,
id,
organizationId,
slug
}))
}
if(!channel.resources) channel.resources = []
let map = new Map()
const categoriesMap = new Map()
const categoryIds2SlugMap = {}
const categoriesSlugsMap = new Map()
const categoriesList = await r
.table('categories')
.getAll(applicationId, {index: 'applicationId'})
for(let i = 0, l = categoriesList.length; i < l; i++) {
const category = categoriesList[i]
if(category.slug.length > 0) {
categoryIds2SlugMap[category.id] = category.slug
categoriesSlugsMap.set(category.slug, category)
categoriesMap.set(category.id, category)
}
}
for(let i = 0, l = channel.resources.length; i < l; i++) {
const categoryIds = []
const resource = channel.resources[i]
const categorySlugs = resource.categorySlugs || []
const digitalAssetIds = []
const digitalAssetMap = {}
const limit = resource.limit || 0
// Convert slugs to ids
if(categorySlugs.length > 0) {
for(let j = 0, k = categorySlugs.length; j < k; j++) {
const slug = categorySlugs[j]
const category = categoriesSlugsMap.get(slug)
if(category && category.id) {
categoryIds.push(category.id)
}
}
}
let options = {
applicationId,
categoryIds,
expand: false,
ignoreDocumentIds,
mode: [],
order: resource.order || {by: 'publishedAt', dir: 'desc'},
status: ['published'],
tags: resource.tags || [],
taxonomyOperator: resource.taxonomyOperator || 'or',
userId: '',
title: '',
}
const order = options.order
if(selectedResource && resource.name === selectedResource) {
let query = r
.table('documents')
.getAll(applicationId, {index: 'applicationId'})
.filter(documentModel._getAllDocFilter(options))
.orderBy(r[order.dir](order.by))
if (limit) {
const skipAmount = (limit * page)
query = query
.skip(skipAmount)
.limit(limit)
}
const res = await query.run()
const docs = []
const docMap = new Map()
for(let j = 0, k = res.length; j < k; j++) {
const doc = res[j]
doc.categories = []
ignoreDocumentIds.push(doc.id)
if(doc.categoryIds && doc.categoryIds.length > 0) {
for(let m = 0, n = doc.categoryIds.length; m < n; m++) {
const categoryId = doc.categoryIds[m]
doc.categories.push(categoriesMap.get(categoryId))
}
}
if(doc.featuredImageId) {
digitalAssetIds.push(doc.featuredImageId)
digitalAssetMap[doc.featuredImageId] = doc.id
}
doc.permalink = documentModel.generatePermalink2(doc)
docMap.set(doc.id, doc)
}
if(digitalAssetIds.length > 0) {
const daRes = await daModel.getAll(digitalAssetIds)
for(let i = 0, l = daRes.length; i < l; i++) {
const da = daRes[i]
const docId = digitalAssetMap[da.id]
const tDoc = docMap.get(docId)
tDoc.featuredImage = da
docMap.set(docId, tDoc)
}
}
for(let [key, value] of docMap) {
docs.push(value)
}
map.set(selectedResource, docs)
// Get just enough to get Id and length of other sources
} else {
let query = r
.table('documents')
.getAll(applicationId, {index: 'applicationId'})
.filter(documentModel._getAllDocFilter(options))
.pluck('id', 'publishedAt')
.orderBy(r[order.dir](order.by))
if(resource.limit) {
query = query.limit(resource.limit)
}
const res = await query.run()
for(let j = 0, k = res.length; j < k; j++) {
const doc = res[j]
ignoreDocumentIds.push(doc.id)
}
}
}
resolve({
channel,
resources: Array.from(map),
//stats
})
} catch(e) {
reject(e)
}
})
}
matchBySlug(slug, options = {}) {
return this.getBySlug.apply(this, arguments)
}
}
module.exports = ChannelModel