superdesk/superdesk-client-core

View on GitHub
scripts/api/article-send.tsx

Summary

Maintainability
C
7 hrs
Test Coverage
import React from 'react';
import {IArticle, IDesk, ISuperdeskQuery, IRestApiResponse} from 'superdesk-api';
import {ISendToDestination} from 'core/interactive-article-actions-panel/interfaces';
import {sdApi} from 'api';
import {extensions} from 'appConfig';
import {notNullOrUndefined, assertNever} from 'core/helpers/typescript-helpers';
import {
    getPublishingDatePatch,
    IPublishingDateOptions,
} from 'core/interactive-article-actions-panel/subcomponents/publishing-date-options';
import {httpRequestJsonLocal} from 'core/helpers/network';
import {notify} from 'core/notify/notify';
import {gettext, getArticleLabel} from 'core/utils';
import {dispatchInternalEvent} from 'core/internal-events';
import {toElasticQuery} from 'core/query-formatting';
import {showModal} from '@superdesk/common';
import {ModalSimple, IModalSimpleAction} from 'core/ui/components/modal-simple';
import {UnorderedList} from 'core/ui/components/UnorderedList';

function getPublishedPackageItems(_package: IArticle): Promise<Array<IArticle>> {
    const query: ISuperdeskQuery = {
        filter: {$and: [{'guid': {$in: sdApi.article.getPackageItemIds(_package)}}]},
        page: 0,
        max_results: 200,
        sort: [{'versioncreated': 'asc'}],
    };

    return httpRequestJsonLocal<IRestApiResponse<IArticle>>({
        method: 'GET',
        path: '/search',
        urlParams: {
            repo: 'published',
            ...toElasticQuery(query),
        },
    }).then((res) => res._items);
}

function applyMiddlewares(
    items: Array<IArticle>,
    destination: ISendToDestination,
): Promise<void> {
    const selectedDeskObj: IDesk | null = (() => {
        if (destination.type === 'desk') {
            return sdApi.desks.getAllDesks().find((desk) => desk._id === destination.desk);
        } else {
            return null;
        }
    })();

    const middlewares = Object.values(extensions)
        .map((ext) => ext?.activationResult?.contributions?.entities?.article?.onSendBefore)
        .filter(notNullOrUndefined);

    return middlewares.reduce(
        (current, next) => {
            return current.then(() => {
                return next(items, selectedDeskObj);
            });
        },
        Promise.resolve(),
    );
}

function confirmSendingPackages(items: Array<IArticle>): Promise<void> {
    const packages = items.filter(({type}) => type === 'composite');

    if (packages.length < 1) {
        return Promise.resolve();
    }

    return new Promise((resolve, reject) => {
        Promise.all(
            packages.map((_package) => getPublishedPackageItems(_package).then((publishedPackageItems) => ({
                _package,
                publishedPackageItems,
            }))),
        ).then((res) => {
            const withPublishedItems = res.filter(({publishedPackageItems}) => publishedPackageItems.length > 0);

            if (withPublishedItems.length < 1) {
                resolve();
            } else {
                showModal(({closeModal}) => {
                    const actions: Array<IModalSimpleAction> = [
                        {
                            label: gettext('Cancel'),
                            onClick: () => {
                                closeModal();
                                reject();
                            },
                        },
                        {
                            label: gettext('Continue'),
                            onClick: () => {
                                closeModal();
                                resolve();
                            },
                            primary: true,
                        },
                    ];

                    return (
                        <ModalSimple title={gettext('Warning')} closeModal={closeModal} footerButtons={actions}>
                            {
                                (() => {
                                    if (withPublishedItems.length === 1) {
                                        const _package = withPublishedItems[0]._package;

                                        return (
                                            <div>
                                                <h3>
                                                    {
                                                        gettext(
                                                            'The package "{{name}}" contains the following '
                                                            + 'published items that can not be sent:',
                                                            {
                                                                name: getArticleLabel(_package),
                                                            },
                                                        )
                                                    }
                                                </h3>

                                                <UnorderedList
                                                    items={withPublishedItems[0].publishedPackageItems.map(
                                                        (item) => getArticleLabel(item),
                                                    )}
                                                />
                                            </div>
                                        );
                                    } else {
                                        return (
                                            <div>
                                                <h3>
                                                    {
                                                        gettext(
                                                            'Some packages contain the following '
                                                            + 'published items that can not be sent:',
                                                        )
                                                    }
                                                </h3>

                                                <br />

                                                {
                                                    withPublishedItems.map(({_package, publishedPackageItems}) => (
                                                        <div key={_package._id}>
                                                            <h3>{getArticleLabel(_package)}</h3>

                                                            <UnorderedList
                                                                items={publishedPackageItems.map(
                                                                    (item) => getArticleLabel(item),
                                                                )}
                                                            />
                                                        </div>
                                                    ))
                                                }
                                            </div>
                                        );
                                    }
                                })()
                            }
                        </ModalSimple>
                    );
                });
            }
        });
    });
}

/**
 * Promise may be rejected by middleware.
 * Returns patches, not whole items.
 */
export function sendItems(
    items: Array<IArticle>,
    selectedDestination: ISendToDestination,
    sendPackageItems: boolean = false,
    publishingDateOptions?: IPublishingDateOptions,
): Promise<Array<Partial<IArticle>>> {
    return applyMiddlewares(items, selectedDestination)
        .then(() => sendPackageItems ? confirmSendingPackages(items) : Promise.resolve())
        .then(() => {
            return Promise.all(
                items.map((item) => {
                    return (() => {
                        if (Object.keys(publishingDateOptions ?? {}).length < 1) {
                            return Promise.resolve({});
                        }

                        /**
                         * If needed, update embargo / publish schedule / time zone
                         */

                        var patch = getPublishingDatePatch(item, publishingDateOptions);

                        if (Object.keys(patch).length > 0) {
                            return httpRequestJsonLocal<IArticle>({
                                method: 'PATCH',
                                path: `/archive/${item._id}`,
                                payload: patch,
                                headers: {
                                    'If-Match': item._etag,
                                },
                            });
                        } else {
                            return Promise.resolve({});
                        }
                    })().then((patch1: Partial<IArticle>) => {
                        const payload = (() => {
                            const basePayload = {};

                            if (sendPackageItems) {
                                basePayload['allPackageItems'] = true;
                            }

                            if (selectedDestination.type === 'personal-space') {
                                return basePayload;
                            } else if (selectedDestination.type === 'desk') {
                                const _payload: Partial<IArticle> = {
                                    ...basePayload,
                                    task: {
                                        desk: selectedDestination.desk,
                                        stage: selectedDestination.stage,
                                    },
                                };

                                return _payload;
                            } else {
                                assertNever(selectedDestination);
                            }
                        })();

                        return httpRequestJsonLocal({
                            method: 'POST',
                            path: `/archive/${item._id}/move`,
                            payload: payload,
                        }).then((patch2: Partial<IArticle>) => {
                            const patchFinal = {
                                ...patch1,
                                ...patch2,
                            };

                            // TODO: fix server response to contain correct links or none at all
                            delete patchFinal['_links'];

                            return patchFinal;
                        }).catch((err) => {
                            notify.error(err._message ?? gettext('Unknown error occurred'));

                            throw err;
                        });
                    });
                }),
            );
        }).then((patches: Array<Partial<IArticle>>) => {
            sdApi.preferences.update('destination:active', selectedDestination);
            notify.success(gettext('Sent successfully'));

            return patches;
        });
}