Source: rpc/SearchServicesJson.js

/*
 * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt
 */
/**
 * @author Trey Roby
 * Date: 3/5/12
 */


import {get, set, pickBy, cloneDeep, has, isUndefined} from 'lodash';
import {ServerParams} from '../data/ServerParams.js';
import {doJsonRequest, DEF_BASE_URL} from '../core/JsonUtils.js';
import {getBgEmail} from '../core/background/BackgroundUtil.js';
import {encodeUrl, download, getModuleName} from '../util/WebUtil.js';

import Enum from 'enum';
import {getTblById, getResultSetID, getResultSetRequest} from '../tables/TableUtil.js';
import {MAX_ROW, DataTagMeta, getTblId, setResultSetID, setResultSetRequest, setSelectInfo} from '../tables/TableRequestUtil.js';
import {SelectInfo} from '../tables/SelectInfo.js';
import {getBackgroundJobs} from '../core/background/BackgroundUtil.js';

export const DownloadProgress= new Enum(['STARTING', 'WORKING', 'DONE', 'UNKNOWN', 'FAIL']);
export const ScriptAttributes= new Enum(['URLsOnly', 'Unzip', 'Ditto', 'Curl', 'Wget', 'RemoveZip']);


const DOWNLOAD_REQUEST = 'downloadRequest';
const SELECTION_INFO = 'selectionInfo';

//TODO: convert FileStatus


/**
 * tableRequest will be sent to the server as a json string.
 * @param {TableRequest} tableRequest is a table request params object
 * @param {number} [hlRowIdx] set the highlightedRow.  default to startIdx.
 * @returns {Promise.<TableModel>}
 * @public
 * @func doFetchTable
 * @memberof firefly.util.table
 */
export function fetchTable(tableRequest, hlRowIdx) {

    const def = {
        startIdx: 0,
        pageSize : MAX_ROW
    };
    
    tableRequest = setupNewRequest(tableRequest, def);

    const params = {
        [ServerParams.REQUEST]: JSON.stringify(tableRequest),
    };

    return doJsonRequest(ServerParams.TABLE_SEARCH, params)
    .then( (tableModel) => {
        const startIdx = get(tableModel, 'request.startIdx', 0);
        if (startIdx > 0) {
            // shift data arrays indices to match partial fetch
            tableModel.tableData.data = tableModel.tableData.data.reduce( (nAry, v, idx) => {
                nAry[idx+startIdx] = v;
                return nAry;
            }, []);
        }
        if (tableModel.selectInfo) {
            // convert selectInfo to JS object
            const selectInfo = SelectInfo.parse(tableModel.selectInfo);
            tableModel.selectInfo = selectInfo.data;
        }

        if (!isUndefined(hlRowIdx)) {
            tableModel.highlightedRow = hlRowIdx;
        } else if (!has(tableModel, 'highlightedRow')) {
            tableModel.highlightedRow = startIdx;
        }
        return tableModel;
    });
}

/**
 * a utility function used to query data from the given tableRequest without altering the table.
 * @param {TableRequest} tableRequest is a table request params object
 * @param {TableRequest} queryRequest filters, sortInfo, and inclCols are used on the tableRequest to return the results
 * @returns {Promise.<number>}
 */
export function queryTable(tableRequest, {filters, sortInfo, inclCols}) {

    const params = Object.assign(pickBy({filters, sortInfo, inclCols}), {[ServerParams.REQUEST]: JSON.stringify(tableRequest)});
    return doJsonRequest(ServerParams.QUERY_TABLE, params)
        .then( (index) => {
            return index;
        });
}

/**
 * returns the table data for the given parameters
 * @param {Object} p  parameters object
 * @param {string[]} p.columnNames an array of column names
 * @param {TableRequest} p.request   location of the file on the server
 * @param {string} p.selectedRows   a comma-separated string of indices of the rows to get the data from
 * @return {Promise<TableModel>}
 */
export function selectedValues({columnNames, request, selectedRows}) {
    columnNames = Array.isArray(columnNames) ? columnNames.join() : String(columnNames);
    selectedRows = Array.isArray(selectedRows) ? selectedRows.join() : String(selectedRows);
    return doJsonRequest(ServerParams.SELECTED_VALUES, {columnNames, request: JSON.stringify(request), selectedRows})
            .then((tableModel) => {
                return tableModel;
            });
}


/**
 *
 * @param {DownloadRequest} dlRequest
 * @param {Object} searchRequest
 * @param {string} selectionInfo
 */
export function packageRequest(dlRequest, searchRequest, selectionInfo) {
    if (!selectionInfo) {
        const {totalRow} = getTblById(searchRequest.tbl_id) || {};
        if (totalRow) {
            selectionInfo = SelectInfo.newInstance({selectAll: true, rowCount: totalRow}).toString();
        }
    }
    if (!dlRequest.Email && getBgEmail()) {
        dlRequest.Email = getBgEmail();
    }
    // insert DataTag if not present
    if (!get(searchRequest, DataTagMeta)) {
        set(searchRequest, DataTagMeta, `${getModuleName()}-${ServerParams.PACKAGE_REQUEST}`);
    }

    const params = {
        [DOWNLOAD_REQUEST]: JSON.stringify(dlRequest),
        [ServerParams.REQUEST]: JSON.stringify(searchRequest),
        [SELECTION_INFO]: selectionInfo
    };

    return doJsonRequest(ServerParams.PACKAGE_REQUEST, params);
}


/**
 *
 * @param {ServerRequest} request
 * @return {Promise}
 */
export function getJsonData(request) {
    const paramList = [];
    paramList.push({name:ServerParams.REQUEST, value: request.toString()});

    return doJsonRequest(ServerParams.JSON_DATA, paramList
    ).then((data) => {return data; });
}

/**
 *
 * @param {ServerRequest} request
 * @param {ServerRequest} clientRequest
 * @param {number} waitMillis
 * @return {Promise}
 */
export function submitBackgroundSearch(request, clientRequest, waitMillis) {
    if (getBgEmail()) {
        request = set(request, ['META_INFO', ServerParams.EMAIL], getBgEmail());
    }
    // insert DataTag if not present
    if (!get(request, DataTagMeta)) {
        set(request, DataTagMeta, `${getModuleName()}-${request.id}`);
    }
    const params = {
        [ServerParams.REQUEST]: JSON.stringify(request),
        [ServerParams.WAIT_MILS]: String(waitMillis)
    };
    clientRequest && (params[ServerParams.CLIENT_REQUEST] = JSON.stringify(clientRequest));

    return doJsonRequest(ServerParams.SUB_BACKGROUND_SEARCH, params);
}

/**
 * add this job to the background
 * @param {string} bgStatus background id
 * @return {Promise}
 */
export function addBgJob(bgStatus) {
    const params = {bgStatus: JSON.stringify(bgStatus)};
    return doJsonRequest(ServerParams.ADD_JOB, params).then( () => true);
}

/**
 *
 * @param {string} id background id
 * @return {Promise}
 */
export function removeBgJob(id) {
    const params = {[ServerParams.ID]: id};
    return doJsonRequest(ServerParams.REMOVE_JOB, params).then( () => true);
}

/**
 *
 * @param {string} id background id
 * @return {Promise}
 */
export function cancel(id) {
    const paramList = [];
    paramList.push({name: ServerParams.ID, value: id});
    return doJsonRequest(ServerParams.CANCEL, paramList
    ).then( () => true);
}

/**
 *
 * @param {string} fileKey
 * @return {Promise}
 */
export function getDownloadProgress(fileKey) {
    const paramList = [];
    paramList.push({name: ServerParams.FILE, value: fileKey});
    return doJsonRequest(ServerParams.DOWNLOAD_PROGRESS, paramList
    ).then((data) => {return DownloadProgress.get(data); });
}


/**
 * @param {string} email
 * @return {Promise}
 */
export function setEmail(email) {
    const idList= Object.keys(getBackgroundJobs() || {});
    const paramList= idList.map( (id) => {
        return {name:ServerParams.ID, value: id};
    } );
    if(paramList.length > 0) {
        paramList.push({name:ServerParams.EMAIL, value:email});
        return doJsonRequest(ServerParams.SET_EMAIL, paramList
        ).then( () => true);
    }
}

/**
 *
 * @param {array|string} ids one id or an array of ids
 * @param {JobAttributes} attribute job attribute
 * @return {Promise}
 */
export function setAttribute(ids, attribute) {
    const idList=  Array.isArray(ids) ? ids : [ids];
    const paramList= idList.map( (id) => {
        return {name:ServerParams.ID, value: id};
    } );
    paramList.push({name:ServerParams.ATTRIBUTE, value:attribute.toString()});
    return doJsonRequest(ServerParams.SET_ATTR, paramList
    ).then( () => true);
}

/**
 * resend email notification to all successfully completed background jobs.
 * @param {string} email
 * @return {Promise}
 */
export function resendEmail(email) {
    return doJsonRequest(ServerParams.RESEND_EMAIL, {[ServerParams.EMAIL]: email}
    ).then( () => true);
}

/**
 *
 * @param {string} id
 * @param {number} idx
 * @return {Promise}
 */
export function clearPushEntry(id, idx ) {
    const paramList = [];
    paramList.push({name: ServerParams.ID, value: id});
    paramList.push({name: ServerParams.IDX, value: `${idx}`});
    return doJsonRequest(ServerParams.CLEAR_PUSH_ENTRY, paramList
    ).then( () => true);
}

/**
 *
 * @param {string} channel
 * @param {string} desc
 * @param {string} data
 * @return {Promise}
 */
export function reportUserAction(channel, desc, data) {
    const paramList = [];
    paramList.push({name: ServerParams.CHANNEL_ID, value: channel});
    paramList.push({name: ServerParams.DATA, value: data});
    paramList.push({name: ServerParams.DESC, value: desc});
    return doJsonRequest(ServerParams.REPORT_USER_ACTION, paramList
    ).then( () => true);
}


/**
 *
 * @param id
 * @param fname
 * @param dataSource
 * @param {array} attributes and array of ScriptAttributes
 * @return {Promise}
 */
export function createDownloadScript(id, fname, dataSource, attributes) {
    const attrAry= Array.isArray(attributes) ? attributes : [attributes];
    const paramList= attrAry.map( (v='') => ({name:ServerParams.ATTRIBUTE, value: v.toString()}) );
    paramList.push({name: ServerParams.ID, value: id});
    paramList.push({name: ServerParams.FILE, value: fname});
    paramList.push({name: ServerParams.SOURCE, value: dataSource});
    return doJsonRequest(ServerParams.CREATE_DOWNLOAD_SCRIPT, paramList);
}

/**
 * Download the download script to the user's computer.
 * @param {Object} p
 * @param {string} p.packageID
 * @param {string} p.type
 * @param {string} p.fname
 * @param {string} p.dataSource
 */
export function getDownloadScript({packageID, type, fname, dataSource}) {
    const url = encodeUrl(DEF_BASE_URL, {packageID, type, fname, dataSource});
    if (url) download(url);
}

/**
 * This function add some important meta info needed by the server to process this request.
 * These meta are system-only info and should not need to be known outside of this app.
 * @param {TableRequest} tableRequest
 * @param {object} defaults
 */
function setupNewRequest(tableRequest, defaults) {
    const newRequest = Object.assign(defaults, cloneDeep(tableRequest));

    const tableModel = getTblById(getTblId(newRequest));
    if (tableModel) {
        // pass along its resultSetID
        const resultSetID = getResultSetID(tableModel.tbl_id);
        resultSetID && setResultSetID(newRequest, resultSetID);
        // pass along its resultSetRequest
        const resultSetRequest = getResultSetRequest(tableModel.tbl_id);
        resultSetRequest && setResultSetRequest(newRequest, resultSetRequest);
        // pass along selectInfo
        tableModel.selectInfo && setSelectInfo(newRequest, tableModel.selectInfo);
    }
    return newRequest;
}