"use strict";
/* TODO auto-generated eslint ignore, please fix! */
/* eslint @typescript-eslint/no-inferrable-types: "off" */
/**
 * Persistent token management
 *
 *  Copyright (C) 2010-2019 R-T Specialty, LLC.
 *
 *  This file is part of the Liza Data Collection Framework.
 *
 *  liza is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.PersistentTokenStore = void 0;
var Token_1 = require("../Token");
/**
 * Persistent token storage
 *
 * This store is used to create, read, and modify tokens.  Its API is
 * designed to constrain state transitions at compile-time.
 *
 * Stores are initialized with a given document id and namespace, and DAOs
 * are initialized with a root field.  Consequently, a new `TokenStore` must
 * be created for each group (namespace) of tokens that needs to be operated
 * on per document.
 *
 * A nullary token id generator must be provided.  Given that it takes no
 * arguments, this means that it is nondeterministic.  This function must
 * generate a unique token id at the namespace level or higher.
 *
 * The philosophy of this store is that any token within a given namespace
 * can be updated at any time, but each namespace has a unique "last" token
 * by document that represents the last token to have been updated within
 * that context.  Also stored is a list of tokens associated with the most
 * recent transition to each state.  When performing any operation on that
 * namespace, information regarding the last tokens will be provided so that
 * the caller can determine whether other tokens within that same context
 * have been modified since a given token was last updated, which may
 * indicate that a token has been superceded by another.
 *
 * As an example, consider the following sequence of events within some
 * namespace "location" for some document 1000:
 *
 *   1. A token `A` is created for a request to a service.  `last` is updated
 *      to point to `A`.  The last `ACTIVE` token is `A`.
 *
 *   2. The user changes information about the location.
 *
 *   3. Another token `B` is created to request information for the new
 *      location data.  `last` is updated to point to `B`.  The last
 *      `ACTIVE` token is `B`.
 *
 *   4. The response for token `A` returns and `A` is updated.  The last
 *      token in the `DONE` state is `A`.
 *
 *   5. The caller for token `A` sees that the has `ACTIVE` token no longer
 *      points to `A` (by observing `last_created`), and so ignores the
 *      reply, understanding that `A` is now stale.
 *
 *   6. The response for  `B` returns and `B` is updated.  The last `DONE`
 *      token is now `B`.
 *
 *   7. The caller notices that `last_created` is _not_ set, and so
 *      proceeds to continue processing token `B`.  The last token in the
 *      `DONE` state is now `B`.
 *
 * For more information on tokens, see `Token`.
 */
var PersistentTokenStore = /** @class */ (function () {
    /**
     * Initialize store
     *
     * @param _dao      data access layer
     * @param _doc_id   constrain store to given document id
     * @param _token_ns token namespace
     * @param _idgen    token id generator
     */
    function PersistentTokenStore(_dao, _doc_id, _token_ns, _idgen) {
        this._dao = _dao;
        this._doc_id = _doc_id;
        this._token_ns = _token_ns;
        this._idgen = _idgen;
    }
    /**
     * Look up an existing token by id
     *
     * This looks up the given token id `token_id` for the document,
     * constrained to this store's namespace and document id.
     *
     * The state of the returned token cannot be determined until runtime,
     * so the caller is responsible for further constraining the type.
     *
     * @param token_id token id
     *
     * @return requested token, if it exists
     */
    PersistentTokenStore.prototype.lookupToken = function (token_id) {
        var _this = this;
        return this._dao
            .getToken(this._doc_id, this._token_ns, token_id)
            .then(function (data) { return _this._tokenDataToToken(data, data.status.type); });
    };
    /**
     * Create a new token for the given document within the store's
     * namespace
     *
     * The returned token will always be `ACTIVE` and will always have
     * `last_mistmatch` set.
     */
    PersistentTokenStore.prototype.createToken = function () {
        var _this = this;
        return this._dao
            .updateToken(this._doc_id, this._token_ns, this._idgen(), Token_1.TokenState.ACTIVE, null)
            .then(function (data) { return _this._tokenDataToToken(data, Token_1.TokenState.ACTIVE, true); });
    };
    /**
     * Convert raw token data to a higher-level `Token`
     *
     * The token state must be provided in addition to the token data for
     * compile-time type checking, where permissable.
     *
     * A token will have `last_mistmatch` set if the last token before a
     * database operation does not match `data.id`.
     *
     * @param data  raw token data
     * @param state token state
     *
     * @return new token
     */
    PersistentTokenStore.prototype._tokenDataToToken = function (data, state, created) {
        if (created === void 0) { created = false; }
        return {
            id: data.id,
            state: state,
            timestamp: data.status.timestamp,
            data: data.status.data,
            last_mismatch: this._isLastMistmatch(data),
            last_created: created || this._isLastCreated(data),
        };
    };
    /**
     * Determine whether the given token data represents a mismatch on the
     * previous last token id
     *
     * For more information on what this means, see `Token.last_mistmatch`.
     *
     * @param data raw token data
     */
    PersistentTokenStore.prototype._isLastMistmatch = function (data) {
        return data.prev_last === null || data.id !== data.prev_last.id;
    };
    /**
     * Whether the token represents the most recently created token
     *
     * @param data raw token data
     *
     * @return whether token was the most recently created
     */
    PersistentTokenStore.prototype._isLastCreated = function (data) {
        return (data.prev_state !== undefined &&
            data.prev_state[Token_1.TokenState.ACTIVE] === data.id);
    };
    /**
     * Complete a token
     *
     * Completing a token places it into a `DONE` state.  Only certain
     * types of tokens can be completed (`TokenStateDoneable`).
     *
     * A token that in a `DONE` state means that processing has completed
     * and is waiting acknowledgement from the system responsible for
     * handling the response.
     *
     * @param src  token to complete
     * @param data optional response data
     *
     * @return token in `DONE` state
     */
    PersistentTokenStore.prototype.completeToken = function (src, data) {
        var _this = this;
        return this._dao
            .updateToken(this._doc_id, this._token_ns, src.id, Token_1.TokenState.DONE, data)
            .then(function (data) { return _this._tokenDataToToken(data, Token_1.TokenState.DONE); });
    };
    /**
     * Acknowledge a token as accepted
     *
     * Accepting a token places it into an `ACCEPTED` state.  Only certain
     * types of tokens can be accepted (`TokenStateAcceptable`).
     *
     * A token that in an `ACCEPTED` state means that a previously completed
     * token has been acknowledged and all resources related to the
     * processing of the token can be freed.
     *
     * @param src  token to accept
     * @param data optional accept reason
     *
     * @return token in `ACCEPTED` state
     */
    PersistentTokenStore.prototype.acceptToken = function (src, data) {
        var _this = this;
        return this._dao
            .updateToken(this._doc_id, this._token_ns, src.id, Token_1.TokenState.ACCEPTED, data)
            .then(function (data) { return _this._tokenDataToToken(data, Token_1.TokenState.ACCEPTED); });
    };
    /**
     * Kill a token
     *
     * Killing a token places it into a `DEAD` state.  Only certain types of
     * tokens can be killed (`TokenStateDeadable`).
     *
     * A token that in a `DEAD` state means that any processing related to
     * that token should be aborted.
     *
     * @param src  token to kill
     * @param data optional kill reason
     *
     * @return token in `DEAD` state
     */
    PersistentTokenStore.prototype.killToken = function (src, data) {
        var _this = this;
        return this._dao
            .updateToken(this._doc_id, this._token_ns, src.id, Token_1.TokenState.DEAD, data)
            .then(function (data) { return _this._tokenDataToToken(data, Token_1.TokenState.DEAD); });
    };
    return PersistentTokenStore;
}());
exports.PersistentTokenStore = PersistentTokenStore;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGVyc2lzdGVudFRva2VuU3RvcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJQZXJzaXN0ZW50VG9rZW5TdG9yZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsb0RBQW9EO0FBQ3BELDBEQUEwRDtBQUMxRDs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRzs7O0FBRUgsa0NBUWtCO0FBTWxCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0RHO0FBQ0g7SUFDRTs7Ozs7OztPQU9HO0lBQ0gsOEJBQ21CLElBQWMsRUFDZCxPQUFtQixFQUNuQixTQUF5QixFQUN6QixNQUFxQjtRQUhyQixTQUFJLEdBQUosSUFBSSxDQUFVO1FBQ2QsWUFBTyxHQUFQLE9BQU8sQ0FBWTtRQUNuQixjQUFTLEdBQVQsU0FBUyxDQUFnQjtRQUN6QixXQUFNLEdBQU4sTUFBTSxDQUFlO0lBQ3JDLENBQUM7SUFFSjs7Ozs7Ozs7Ozs7O09BWUc7SUFDSCwwQ0FBVyxHQUFYLFVBQVksUUFBaUI7UUFBN0IsaUJBSUM7UUFIQyxPQUFPLElBQUksQ0FBQyxJQUFJO2FBQ2IsUUFBUSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUM7YUFDaEQsSUFBSSxDQUFDLFVBQUEsSUFBSSxJQUFJLE9BQUEsS0FBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUE5QyxDQUE4QyxDQUFDLENBQUM7SUFDbEUsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNILDBDQUFXLEdBQVg7UUFBQSxpQkFVQztRQVRDLE9BQU8sSUFBSSxDQUFDLElBQUk7YUFDYixXQUFXLENBQ1YsSUFBSSxDQUFDLE9BQU8sRUFDWixJQUFJLENBQUMsU0FBUyxFQUNkLElBQUksQ0FBQyxNQUFNLEVBQUUsRUFDYixrQkFBVSxDQUFDLE1BQU0sRUFDakIsSUFBSSxD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