"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataProcessor = void 0;
/**
 * Process data provided by the client
 *
 * TOOD: This contains Data API and bucket merging logic that is better done
 * elsewhere.
 */
var DataProcessor = /** @class */ (function () {
    /**
     * Initialize processor
     *
     * The staging bucket constructor will be used to wrap the bucket for
     * diff-related operations.
     *
     * @param filter       - bucket filter
     * @param dapif        - data API constructor
     * @param meta_source  - metadata source
     * @param staging_ctor - staging bucket constructor
     */
    function DataProcessor(_filter, _dapif, _meta_source, _stagingCtor) {
        this._filter = _filter;
        this._dapif = _dapif;
        this._meta_source = _meta_source;
        this._stagingCtor = _stagingCtor;
    }
    /**
     * Process client-provided data diff
     *
     * This performs sanitization to ensure that we are storing only
     * "correct" data within our database. This also strips any unknown
     * bucket values, preventing users from using us as their own personal
     * database.
     *
     * @param data    - bucket diff data
     * @param request - submitting request
     * @param program - active program
     *
     * @return processed diff
     */
    DataProcessor.prototype.processDiff = function (data, request, program, bucket, quote) {
        var filtered = this.sanitizeDiff(data, request, program);
        var dapi_manager = this._dapif(program.apis, request, quote);
        var staging = this._stagingCtor(bucket);
        // forbidBypass will force diff generation on initQuote
        staging.setValues(filtered);
        staging.forbidBypass();
        program.initQuote(staging, true);
        var diff = staging.getDiff();
        var rdiff = {};
        // array of promises for any dapi requests
        var _a = this._triggerDapis(dapi_manager, program, diff, staging), dapis = _a[0], meta_clear = _a[1];
        for (var diff_key in diff) {
            rdiff[diff_key] = staging.getOriginalDataByName(diff_key);
        }
        staging.commit();
        return {
            filtered: filtered,
            dapis: dapis,
            meta_clear: meta_clear,
            rdiff: rdiff,
        };
    };
    /**
     * Sanitize client-provided data
     *
     * Internal fields will be stripped if the session is not
     * internal.  Following that, the filter provided via the ctor will be
     * applied.
     *
     * `permit_null` should be used only in the case of bucket diffs, which
     * contain nulls as terminators.
     *
     * @param data    - client-provided data
     * @param request - client request
     * @param program - active program
     *
     * @return filtered data
     */
    DataProcessor.prototype.sanitizeDiff = function (data, request, program) {
        if (!request.getSession().isInternal()) {
            this._cleanInternals(data, program);
        }
        return this._filter.filter(data, program.meta.qtypes, {}, true);
    };
    /**
     * Strip internal fields from diff `data`
     *
     * Internal fields are defined by the program `program`.
     *
     * @param data    - bucket diff data
     * @param program - active program
     */
    DataProcessor.prototype._cleanInternals = function (data, program) {
        for (var id in program.internal) {
            delete data[id];
        }
    };
    /**
     * Trigger metadata Data API requests
     *
     * @param dapi_manager - dapi manager
     * @param program      - active program
     * @param data         - client-provided data
     * @param bucket       - active bucket
     *
     * @return an array containing the dapis and cleared meta values
     */
    DataProcessor.prototype._triggerDapis = function (dapi_manager, program, data, bucket) {
        var _this = this;
        var _a = program.mapis, mapis = _a === void 0 ? {} : _a, _b = program.meta.fields, fields = _b === void 0 ? {} : _b;
        var dapi_fields = this._determineDapiFields(mapis, data);
        var clear = this._genClearMetaValues(dapi_fields);
        var dapis = Object.keys(dapi_fields)
            .map(function (field) {
            var dapi = fields[field].dapi;
            var indexes = dapi_fields[field];
            return indexes.map(function (i) {
                return _this._meta_source.getFieldData(field, i, dapi_manager, dapi, _this._mapDapiData(dapi, bucket, i, data));
            });
        })
            .reduce(function (result, x) { return result.concat(x); }, []);
        return [dapis, clear];
    };
    /**
     * Generate update to clear metadata fields with pending dapi calls
     *
     * This ensures that stale data won't be accessible to systems while a
     * request hasn't yet completed.  For example, if performing a rate
     * lookup, it wouldn't be desirable to use an old rate even though data
     * used to retrieve it has since changed.
     *
     * @param fields - field names and array of indexes
     *
     * @return cleared values
     */
    DataProcessor.prototype._genClearMetaValues = function (fields) {
        return Object.keys(fields).reduce(function (result, field) {
            result[field] = fields[field].reduce(function (values, i) {
                values[i] = '';
                return values;
            }, []);
            return result;
        }, {});
    };
    /**
     * Determine which fields require a Data API to be triggered
     *
     * @param mapis - metadata dapi descriptors
     * @param data  - client-provided data
     *
     * @return fields with indexes in need of dapi calls
     */
    DataProcessor.prototype._determineDapiFields = function (mapis, data) {
        return Object.keys(mapis).reduce(function (result, src_field) {
            var fdata = data[src_field];
            if (fdata === undefined) {
                return result;
            }
            var fields = mapis[src_field];
            // get each index that changed
            fields.forEach(function (field) {
                result[field] = result[field] || [];
                Object.keys(fdata).forEach(function (i) {
                    if (fdata[i] === undefined || fdata[i] === null) {
                        return;
                    }
                    result[field][i] = i;
                });
            });
            return result;
        }, {});
    };
    /**
     * Map data from bucket to dapi inputs
     *
     * @param dapi      - Data API descriptor
     * @param bucket    - active (source) bucket
     * @param index     - field index
     * @param diff_data - client-provided data
     *
     * @return key/value dapi input data
     */
    DataProcessor.prototype._mapDapiData = function (dapi, bucket, index, diff_data) {
        var mapsrc = dapi.mapsrc;
        return Object.keys(mapsrc).reduce(function (result, srcid) {
            var bucketid = mapsrc[srcid];
            var bdata = (diff_data[bucketid] || [])[index] ||
                (bucket.getDataByName(bucketid) || [])[index];
            if (typeof bdata === 'undefined') {
                // This might be better as an empty string, but we will
                // need to look into it more before we change it. It has
                // been an empty array for a long time and it might cause
                // issues to change it now.
                result[srcid] = [];
            }
            else {
                result[srcid] = bdata;
            }
            return result;
        }, {});
    };
    return DataProcessor;
}());
exports.DataProcessor = DataProcessor;
//# sourceMappingURL=data:application/json;base64, 