superdesk/superdesk-client-core

View on GitHub
scripts/apps/search/SavedSearch.ts

Summary

Maintainability
C
1 day
Test Coverage
import {CronTimeInterval} from 'types/DataStructures/TimeInterval';

import {forOwn} from 'lodash';
import {mapPredefinedDateFiltersClientToServer} from 'apps/search/directives/DateFilters';
import {setFilters} from './services/SearchService';
import {IUser, IDesk} from 'superdesk-api';

export interface IUserSubscription {
    user: IUser['_id'];
    scheduling: CronTimeInterval;
    next_report?: any;
}

export interface IDeskSubscription {
    desk: IDesk['_id'];
    scheduling: CronTimeInterval;
    next_report?: any;
}

export interface ISavedSearch {
    _id?: string;
    name: string;
    description: string;
    is_global: boolean;

    /**
     * When true it will display Saved Search in search dropdown menu as a shortuct
     */
    shortcut: boolean;

    filter: any;
    user: IUser['_id'];
    subscribers: {
        user_subscriptions: Array<IUserSubscription>;
        desk_subscriptions: Array<IDeskSubscription>;
    };
}

// TODO: implement diffing
// server doesn't allow read-only fields in patch request
const removeReadOnlyUserSubscriberFields = (subscription: IUserSubscription): IUserSubscription => ({
    user: subscription.user,
    scheduling: subscription.scheduling,
});

// TODO: implement diffing
// server doesn't allow read-only fields in patch request
const removeReadOnlyDeskSubscriberFields = (subscription: IDeskSubscription): IDeskSubscription => ({
    desk: subscription.desk,
    scheduling: subscription.scheduling,
});

export const isUserSubscribedToSavedSearch = (
    _savedSearch: ISavedSearch,
    userId: IUser['_id'],
    getDesk: (id: IDesk['_id']) => IDesk,
): boolean => {
    const {subscribers} = _savedSearch;

    if (subscribers == null) {
        return false;
    }

    if (subscribers.user_subscriptions.some((subscription) => subscription.user === userId)) {
        return true;
    }

    if (subscribers.desk_subscriptions.some((subscription) => {
        const desk: IDesk = getDesk(subscription.desk);

        return desk.members.includes(userId);
    })) {
        return true;
    } else {
        return false;
    }
};

export const updateSubscribers = (
    savedSearch: ISavedSearch,
    nextSubscribers: ISavedSearch['subscribers'],
    api: any,
): Promise<ISavedSearch> => {
    const savedSearchNext: ISavedSearch = {
        ...savedSearch,
        subscribers: nextSubscribers,
    };

    return saveOrUpdateSavedSearch(api, savedSearch, savedSearchNext);
};

export const unsubscribeUser = (
    savedSearch: ISavedSearch,
    userId: IUser['_id'],
    api: any,
): Promise<ISavedSearch> => {
    const nextSubscribers: ISavedSearch['subscribers'] = {
        ...savedSearch.subscribers,
        user_subscriptions: savedSearch.subscribers.user_subscriptions.filter(
            (subscription) => subscription.user !== userId,
        ),
    };

    return updateSubscribers(savedSearch, nextSubscribers, api);
};

export const unsubscribeDesk = (
    savedSearch: ISavedSearch,
    deskId: IDesk['_id'],
    api: any,
): Promise<ISavedSearch> => {
    const nextSubscribers: ISavedSearch['subscribers'] = {
        ...savedSearch.subscribers,
        desk_subscriptions: savedSearch.subscribers.desk_subscriptions.filter(
            (subscription) => subscription.desk !== deskId,
        ),
    };

    return updateSubscribers(savedSearch, nextSubscribers, api);
};

export function mapFiltersServerToClient(savedSearch: ISavedSearch) {
    if (typeof savedSearch.filter === 'object' && savedSearch.filter.hasOwnProperty('query')) {
        savedSearch.filter.query = setFilters(savedSearch.filter.query);

        // after saving the item, filter value is string similar to
        // filter: "{"query": {"desk": "[\"5c04e596149f111ebc9d3da3\"]", "spike": "exclude"}}"

        // This check can be replaced with `!= null` check after SDESK-3698 is merged
    }

    return savedSearch;
}

function mapFiltersClientToServer(search) {
    let nextSearch = {...search};

    forOwn(nextSearch, (value, key) => {
        if (['priority', 'urgency'].includes(key)) {
            // Convert integer fields: priority and urgency to objects
            nextSearch[key] = JSON.parse(value);
        }
    });

    nextSearch = mapPredefinedDateFiltersClientToServer(nextSearch);

    return nextSearch;
}

export function saveOrUpdateSavedSearch(api, savedSearchOriginal: ISavedSearch, savedSearchChanged: ISavedSearch) {
    if (savedSearchChanged.filter != null && savedSearchChanged.filter.query != null) {
        savedSearchChanged.filter.query = mapFiltersClientToServer(savedSearchChanged.filter.query);
    }

    if (savedSearchChanged.subscribers != null) {
        if (savedSearchChanged.subscribers.user_subscriptions != null) {
            savedSearchChanged.subscribers.user_subscriptions = savedSearchChanged.subscribers.user_subscriptions
                .map(removeReadOnlyUserSubscriberFields);
        }

        if (savedSearchChanged.subscribers.desk_subscriptions != null) {
            savedSearchChanged.subscribers.desk_subscriptions = savedSearchChanged.subscribers.desk_subscriptions
                .map(removeReadOnlyDeskSubscriberFields);
        }
    }

    return api('saved_searches').save(savedSearchOriginal, savedSearchChanged)
        .then(mapFiltersServerToClient);
}