superdesk/superdesk-client-core

View on GitHub
scripts/apps/publish/controllers/PublishQueueController.ts

Summary

Maintainability
F
3 days
Test Coverage
import _ from 'lodash';
import {isSuperdeskContent} from 'apps/workspace/helpers/isSuperdeskContent';
import {gettext} from 'core/utils';

PublishQueueController.$inject = [
    '$scope',
    'subscribersService',
    'api',
    '$q',
    'notify',
    '$location',
    'ingestSources',
    'vocabularies',
];

export function PublishQueueController($scope, subscribersService, api, $q, notify, $location, ingestSources,
    vocabularies) {
    $scope.subscribers = null;
    $scope.subscriberLookup = {};
    $scope.ingestProviders = null;
    $scope.ingestProvidersLookup = {};
    $scope.publish_queue = [];
    $scope.contentTypes = null;
    $scope.selectedFilterSubscriber = null;
    $scope.selectedFilterIngestProvider = null;
    $scope.selectedFilterContentType = null;
    $scope.multiSelectCount = 0;
    $scope.selectedQueueItems = [];
    $scope.showResendBtn = false;
    $scope.showCancelBtn = false;
    $scope.showCancelSelectionBtn = false;
    $scope.queueSearch = false;
    $scope.selected = {};
    $scope.publish_queue_statuses = ['pending', 'in-progress', 'success', 'error', 'retrying', 'failed', 'canceled'];
    $scope.pagination = {page: 1};
    $scope.pageSize = 25;

    $scope.$watch('pagination.page', () => {
        $scope.reload();
    });

    var promises = [];

    promises.push(subscribersService.fetchSubscribers().then((items) => {
        $scope.subscribers = items;
        $scope.subscriberLookup = _.keyBy(items, '_id');
    }));

    promises.push(ingestSources.fetchAllIngestProviders().then((items) => {
        $scope.ingestProviders = items;
        $scope.ingestProvidersLookup = _.keyBy($scope.ingestProviders, '_id');
    }));

    promises.push(vocabularies.getVocabulary('type').then((items) => {
        $scope.contentTypes = items.items;
    }));

    /*
    * Get search input from search box to search for headline or unique_name,
    * and perfrom reload function to populate publish queue.
    */
    $scope.search = function(query) {
        $scope.searchQuery = query;
        $scope.pagination.page = 1;
        $scope.reload();
    };

    /*
    * Populates the publish queue and update the flags after fetch operation.
    */
    function populatePublishQueue() {
        return fetchPublishQueue().then((queue) => {
            var queuedItems = queue._items;

            _.forEach(queuedItems, (item) => {
                angular.extend(item, {selected: false});
            });

            $scope.publish_queue = queuedItems;
            $scope.lastRefreshedAt = new Date();
            $scope.showResendBtn = false;
            $scope.showCancelSelectionBtn = false;
            $scope.maxPage = Math.ceil(queue._meta.total / $scope.pageSize);
        }).finally(() => {
            $scope.loading = false;
        });
    }
    /*
    * Fetch the publish queue on the basis of built criteria.
    */
    function fetchPublishQueue() {
        var criteria = criteria || {};

        criteria.max_results = $scope.pageSize;
        criteria.page = $scope.pagination.page;

        var orTerms = null;

        if (!_.isEmpty($scope.searchQuery)) {
            orTerms = {$or: [
                {headline: {
                    $regex: $scope.searchQuery,
                    $options: 'i'},
                }, {unique_name: $scope.searchQuery},
            ]};
        }

        var filterTerms = [];

        if (!_.isNil($scope.selectedFilterSubscriber)) {
            filterTerms.push({subscriber_id: $scope.selectedFilterSubscriber._id});
        }
        if (!_.isNil($scope.selectedFilterStatus)) {
            filterTerms.push({state: $scope.selectedFilterStatus});
        }

        if (!_.isNil($scope.selectedFilterIngestProvider)) {
            filterTerms.push({ingest_provider: $scope.selectedFilterIngestProvider._id});
        }

        if (!_.isNil($scope.selectedFilterContentType)) {
            filterTerms.push({content_type: $scope.selectedFilterContentType.qcode});
        }

        var andTerms = [];

        _.each(filterTerms, (term) => {
            andTerms.push(term);
        });

        if (orTerms !== null) {
            andTerms.push(orTerms);
        }

        if (!_.isEmpty(andTerms)) {
            criteria.where = JSON.stringify({
                $and: andTerms,
            });
        }
        return api.publish_queue.query(criteria);
    }

    $scope.reload = function() {
        $scope.loading = true;
        $q.all(promises).then(() => {
            populatePublishQueue();
            previewItem();
        });
    };

    $scope.buildNewSchedule = function(item) {
        var pickFields = ['item_id', 'item_version', 'publishing_action', 'formatted_item', 'headline',
            'content_type', 'subscriber_id', 'unique_name', 'destination', 'ingest_provider',
            'item_encoding', 'encoded_item_id', 'associated_items'];

        var newItem = _.pick(item, pickFields);

        return newItem;
    };

    $scope.scheduleToSend = function(item) {
        var queueItems = [];

        if (angular.isDefined(item)) {
            queueItems.push($scope.buildNewSchedule(item));
        } else if ($scope.multiSelectCount > 0) {
            _.forEach($scope.selectedQueueItems, (_item) => {
                queueItems.push($scope.buildNewSchedule(_item));
            });
        }

        api.publish_queue.save([], queueItems).then(
            (response) => {
                $scope.reload();
                $scope.cancelSelection();
            },
            (response) => {
                if (angular.isDefined(response.data._issues)) {
                    if (angular.isDefined(response.data._issues['validator exception'])) {
                        notify.error(gettext('Error: ' + response.data._issues['validator exception']));
                    }
                } else {
                    notify.error(gettext('Error: Failed to re-schedule'));
                }
            },
        );
    };

    $scope.cancelSend = function(item) {
        var itemList = [];

        if (angular.isDefined(item)) {
            itemList.push(item);
        } else if ($scope.multiSelectCount > 0) {
            _.forEach($scope.selectedQueueItems, (_item) => {
                if (_item.state === 'pending' || _item.state === 'retrying') {
                    _item.state = 'canceled';
                    itemList.push(_item);
                }
            });
        }

        _.forEach(itemList, (_item) => {
            api.publish_queue.update(_item, {state: 'canceled'});
        });
        $scope.cancelSelection();
    };

    $scope.filterPublishQueue = function(item, type) {
        switch (type) {
        case 'subscriber':
            $scope.selectedFilterSubscriber = item;
            break;
        case 'ingest_provider':
            $scope.selectedFilterIngestProvider = item;
            break;
        case 'status':
            $scope.selectedFilterStatus = item;
            break;
        case 'type':
            $scope.selectedFilterContentType = item;
            break;
        default:
            $scope.selectedFilterSubscriber = null;
            $scope.selectedFilterIngestProvider = null;
            $scope.selectedFilterStatus = null;
            $scope.selectedFilterContentType = null;
        }
        populatePublishQueue();
        $scope.multiSelectCount = 0;
        $scope.selectedQueueItems = [];
        $scope.pagination.page = 1;
    };

    $scope.selectQueuedItem = function(queuedItem) {
        if (queuedItem.selected) {
            $scope.selectedQueueItems = _.union($scope.selectedQueueItems, [queuedItem]);
        } else {
            $scope.selectedQueueItems = _.without($scope.selectedQueueItems, queuedItem);
        }

        /* look for any items in states that cannot be resent */
        var idx = _.findIndex($scope.selectedQueueItems,
            (item: any) => _.includes(['pending', 'in-progress', 'retrying'], item.state));

        /* All selected items can be resent */
        if (idx === -1) {
            $scope.showResendBtn = true;
            $scope.showCancelBtn = false;
            $scope.showCanceSelectionlBtn = false;
        } else {
            /* Find the index of any item that can be resent */
            idx = _.findIndex($scope.selectedQueueItems,
                (item: any) => item.state === 'success' || item.state === 'in-progress' || item.state === 'canceled' ||
                    item.state === 'error' || item.state === 'retrying');
            /* Nothing to resend found */
            if (idx === -1) {
                $scope.showResendBtn = false;
                $scope.showCancelSelectionBtn = true;
                /* look for items that can be canceled */
                idx = _.findIndex($scope.selectedQueueItems,
                    (item: any) => item.state === 'pending' || item.state === 'retrying');
                /* Something can be canceled so show the button */
                if (idx !== -1) {
                    $scope.showCancelBtn = true;
                } else {
                    $scope.showCancelBtn = false;
                }
            } else {
                $scope.showResendBtn = false;
                $scope.showCancelBtn = false;
                $scope.showCancelSelectionBtn = false;
            }
        }

        $scope.multiSelectCount = $scope.selectedQueueItems.length;
    };

    $scope.cancelSelection = function() {
        $scope.selectedQueueItems = [];
        $scope.multiSelectCount = 0;
        populatePublishQueue();
    };

    function refreshQueueState(data) {
        var item = _.find($scope.publish_queue, {_id: data.queue_id});

        if (item) {
            var fields = ['error_message', 'completed_at', 'state'];

            angular.extend(item, _.pick(data, fields));
            $scope.$apply();
        }
    }

    $scope.preview = function(queueItem) {
        $location.search('_id', queueItem ? queueItem._id : queueItem);
    };

    function previewItem() {
        var queueItem: any = _.find($scope.publish_queue, {_id: $location.search()._id}) || null;

        $scope.selected.extensionPoint = false;

        if (queueItem) {
            let endpoint = 'archive';

            if (isSuperdeskContent(queueItem.content_type)) {
                if (queueItem.publishing_action === 'being_corrected') {
                    endpoint = 'published';
                }
                api.find(endpoint, queueItem.item_id, {version: queueItem.item_version})
                    .then((item) => {
                        $scope.selected.preview = item;
                    });
            } else {
                $scope.selected.preview = queueItem;
                $scope.selected.extensionPoint = true;
            }
        } else {
            $scope.selected.preview = null;
        }
    }

    $scope.$on('$routeUpdate', previewItem);

    $scope.$on('publish_queue:update', (evt, data) => {
        refreshQueueState(data);
    });
    $scope.reload();
}