public/js/actions/AtomActions/getSuggestionsForLatestContent.js (119 lines of code) (raw):
import {getTagsForContent} from '../../services/capi';
import {mostViewed} from '../../services/ophan';
import {fetchTargetsForTags} from '../../services/TargetingApi';
import AtomsApi from '../../services/AtomsApi';
import {atomPropType} from '../../constants/atomPropType.js';
import {logError} from '../../util/logger';
import {PropTypes} from 'prop-types';
function requestSuggestionsForLatestContent() {
return {
type: 'SUGGESTIONS_FOR_LATEST_CONTENT_REQUEST',
receivedAt: Date.now()
};
}
function receiveSuggestionsForLatestContent(suggestionsForLatestContent) {
return {
type: 'SUGGESTIONS_FOR_LATEST_CONTENT_RECEIVE',
suggestionsForLatestContent,
receivedAt: Date.now()
};
}
function errorReceivingSuggestionsForLatestContent(error) {
logError(error);
return {
type: 'SHOW_ERROR',
message: 'Could not get suggests for latest content',
error: error,
receivedAt: Date.now()
};
}
const distinct = array => array.filter((value, index, self) => self.indexOf(value) === index);
const flatten = array => [].concat.apply([], array);
//Returns set of all tags in the given content
function getTags(contentArray) {
return distinct(flatten(
contentArray.map(content => content.tags.filter(tag => tag.type === "keyword"))
));
}
//Returns an array of target atoms for the given tags
function getTargets(tags) {
const tagIds = tags.filter(tag => tag.type === "keyword").map(tag => tag.id);
return fetchTargetsForTags(tagIds).then(targets => {
return targets.filter(target => target.url.includes("/atom/"));
});
}
const AllSnippets = ["guide","qanda","timeline","profile"];
function isSnippet(atomType) {
return AllSnippets.indexOf(atomType.toLowerCase()) >= 0;
}
function getAtomUrlToAtom(targets) {
const atomUrls = distinct(flatten(Object.values(targets.map(target => target.url))));
return Promise.all(atomUrls.map(getAtomFromTargetUrl))
.then(atoms => {
const atomUrlToAtom = {};
atoms.forEach(atom => {
if (isSnippet(atom.atomType)) atomUrlToAtom[atom.url] = atom;
});
return atomUrlToAtom;
});
}
function getAtomFromTargetUrl(url) {
// the path has the form: /atoms/<type>/<id>
const tokens = url.split('/');
const atomType = tokens[tokens.length-2];
const atomId = tokens[tokens.length-1];
return AtomsApi.getAtom(atomType, atomId)
.then(res => res.json()).then(atom => {
atom.url = url;
return atom;
});
}
function getTagToUrls(targets) {
const tagToUrls = {};
targets.forEach(target => {
target.tagPaths.forEach(tag => {
tagToUrls[tag] ? tagToUrls[tag].concat([target.url]) : tagToUrls[tag] = [target.url];
});
});
return tagToUrls;
}
export const SuggestedAtomsPropType = PropTypes.shape({
contentId: PropTypes.string.isRequired,
headline: PropTypes.string.isRequired,
internalComposerCode: PropTypes.string.isRequired,
atoms: PropTypes.arrayOf(atomPropType).isRequired
});
function resolveContentAtoms(contentArray, tagToUrls, urlToAtom) {
return contentArray.map(content => {
const urls = content.tags.map(tag => tagToUrls[tag.id]).filter(urls => urls !== undefined);
const atoms = distinct(flatten(urls)).map(url => urlToAtom[url]).filter(atom => atom !== undefined);
return {
contentId: content.id,
headline: content.fields.headline,
internalComposerCode: content.fields.internalComposerCode,
atoms: atoms
};
});
}
const skipChars = "https://www.theguardian.com/".length;
/**
* Returns an array of SuggestedAtomsPropType, which maps content
* to suggested snippet atoms.
* The content is taken from ophan's most-viewed list.
*/
export function getSuggestionsForLatestContent() {
return dispatch => {
dispatch(requestSuggestionsForLatestContent());
return mostViewed()
.then(mostViewedContent =>
Promise.all(mostViewedContent.map(content => getTagsForContent(content.url.slice(skipChars))))
)
//Filter out any articles that already contain a snippet atom
.then(contentArray => contentArray.filter(content => !content.atoms || content.atoms.length === 0))
.then(contentArray => {
const tags = getTags(contentArray);
const targetsPromise = getTargets(tags);
const urlToAtomPromise = targetsPromise.then(getAtomUrlToAtom);
return Promise.all([targetsPromise, urlToAtomPromise])
.then(([targets, urlToAtom]) => {
const tagToUrls = getTagToUrls(targets);
return resolveContentAtoms(contentArray, tagToUrls, urlToAtom);
});
})
.then(results => dispatch(receiveSuggestionsForLatestContent(results)))
.catch(error => {
dispatch(errorReceivingSuggestionsForLatestContent(error));
});
};
}