scripts/apps/search/SavedSearch.ts
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);
}