"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 mapper_1 = require("../../../mapper");
const MAX_BATCH_WRITE_ITEMS = 25;
class BatchWriteSingleTableRequest {
    constructor(dynamoRx, modelClazz, tableName) {
        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.Mapper.createToKeyFn(this.modelClazz);
        }
        return this._keyFn;
    }
    delete(items) {
        this.itemsToProcess.push(...items.map(item => ({ DeleteRequest: { Key: this.toKey(item) } })));
        return this;
    }
    put(items) {
        this.itemsToProcess.push(...items.map(item => ({ PutRequest: { Item: mapper_1.Mapper.toDb(item, this.modelClazz) } })));
        return this;
    }
    execNextBatch() {
        const batch = this.itemsToProcess.splice(0, MAX_BATCH_WRITE_ITEMS);
        const batchWriteItemInput = {
            RequestItems: {
                [this.tableName]: batch,
            },
        };
        return this.dynamoRx.batchWriteItem(batchWriteItemInput).pipe(operators_1.tap((batchWriteManyResponse) => {
            if (batchWriteManyResponse.UnprocessedItems) {
                this.itemsToProcess.unshift(...batchWriteManyResponse.UnprocessedItems[this.tableName]);
            }
        }), operators_1.map((batchWriteManyResponse) => ({
            remainingItems: this.itemsToProcess.length,
            capacityExceeded: !!batchWriteManyResponse.UnprocessedItems,
            consumedCapacity: batchWriteManyResponse.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) {
        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;
            }
            return rxjs_1.of(r).pipe(operators_1.delay(backoffTime * throttleTimeSlot));
        }), 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