"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const helper_1 = require("../../../helper");
const logger_1 = require("../../../logger/logger");
const mapper_1 = require("../../../mapper");
const MAX_BATCH_WRITE_ITEMS = 25;
class BatchWriteSingleTableRequest {
    constructor(dynamoRx, modelClazz, tableName) {
        this.logger = logger_1.createLogger('dynamo.request.BatchWriteSingleTableRequest', modelClazz);
        this.dynamoRx = dynamoRx;
        if (modelClazz === null || modelClazz === undefined) {
            throw new Error("please provide the model clazz for the request, won't work otherwise");
        }
        this.modelClazz = modelClazz;
        this.tableName = tableName;
        this.itemsToProcess = [];
    }
    get toKey() {
        if (!this.keyFn) {
            this.keyFn = mapper_1.createToKeyFn(this.modelClazz);
        }
        return this.keyFn;
    }
    delete(items) {
        this.itemsToProcess.push(...items.map(item => ({ DeleteRequest: { Key: this.toKey(item) } })));
        this.logger.debug(`${items.length} items added for DeleteRequest`);
        return this;
    }
    put(items) {
        this.itemsToProcess.push(...items.map(item => ({ PutRequest: { Item: mapper_1.toDb(item, this.modelClazz) } })));
        this.logger.debug(`${items.length} items added for PutRequest`);
        return this;
    }
    execNextBatch() {
        const batch = this.itemsToProcess.splice(0, MAX_BATCH_WRITE_ITEMS);
        const batchWriteItemInput = {
            RequestItems: {
                [this.tableName]: batch,
            },
        };
        this.logger.debug('request', batchWriteItemInput);
        return this.dynamoRx.batchWriteItem(batchWriteItemInput).pipe(operators_1.tap(response => this.logger.debug('response', response)), operators_1.tap((batchWriteManyResponse) => {
            if (batchWriteManyResponse.UnprocessedItems && batchWriteManyResponse.UnprocessedItems[this.tableName]) {
                this.itemsToProcess.unshift(...batchWriteManyResponse.UnprocessedItems[this.tableName]);
            }
        }), operators_1.map((batchWriteManyResponse) => ({
            remainingItems: this.itemsToProcess.length,
            capacityExceeded: !!(batchWriteManyResponse.UnprocessedItems && batchWriteManyResponse.UnprocessedItems[this.tableName]),
            consumedCapacity: batchWriteManyResponse.ConsumedCapacity,
        })), operators_1.tap(response => {
            if (response.capacityExceeded) {
                this.logger.info('capacity exceeded', response.consumedCapacity);
            }
        }));
    }
    /**
     *
     * @param backoffTimer generator for how much timeSlots should be waited before requesting next batch. only used when capacity was exceeded. default randomExponentialBackoffTimer
     * @param throttleTimeSlot defines how long one timeSlot is for throttling, default 1 second
     */
    exec(backoffTimer = helper_1.randomExponentialBackoffTimer, throttleTimeSlot = 1000) {
        this.logger.debug('starting batchWriteItem');
        let rBoT = backoffTimer();
        let backoffTime = 0;
        return this.execNextBatch().pipe(operators_1.mergeMap((r) => {
            if (!r.capacityExceeded) {
                rBoT = backoffTimer();
                backoffTime = 0;
            }
            else {
                backoffTime = rBoT.next().value * throttleTimeSlot;
                this.logger.info(`wait ${backoffTime} ms until next request`, { backoffTime });
            }
            return rxjs_1.of(r).pipe(operators_1.delay(backoffTime));
        }), operators_1.mergeMap((r) => {
            if (r.remainingItems > 0) {
                return this.exec();
            }
            else {
                return rxjs_1.of();
            }
        }));
    }
}
exports.BatchWriteSingleTableRequest = BatchWriteSingleTableRequest;
//# sourceMappingURL=batch-write-single-table.request.js.map