redeem proffered tickets, db schema 1.1.0
[squeep-indie-auther] / src / db / abstract.js
index 26867e45f35ca8b50b0c15729e7421f9c5ad1648..d5367df933c109f632140517b55790deb0251045 100644 (file)
@@ -38,13 +38,16 @@ class Database {
   }
 
 
+  /**
+   * @typedef {Object} SchemaVersionObject
+   * @property {Number} major
+   * @property {Number} minor
+   * @property {Number} patch
+   */
   /**
    * Query the current schema version.
    * This is a standalone query function, as it is called before statements are loaded.
-   * @returns {Object} version
-   * @returns {Number} version.major
-   * @returns {Number} version.minor
-   * @returns {Number} version.patch
+   * @returns {Promise<SchemaVersionObject>}
    */
   async _currentSchema() {
     this._notImplemented('_currentSchema', arguments);
@@ -78,31 +81,26 @@ class Database {
   }
 
 
-  /**
-   * @param {*} x
-   * @returns {Boolean}
-   */
-  static _isUUID(x) {
-    try {
-      uuid.parse(x);
-      return true;
-    } catch (e) {
-      return false;
-    }
-  }
-
-
-  /**
-   * @param {*} x
-   * @returns {Boolean}
-   */
-  static _isInfinites(x) {
-    return typeof(x) === 'number'
-      && Math.abs(x) === Infinity;
-  }
-
   /**
    * Basic type checking of object properties.
+   *
+   * Types may be any of the built-in types:
+   * - boolean
+   * - bigint (also allowed with 'number')
+   * - function
+   * - number (this will also allow 'bigint')
+   * - object
+   * - string
+   * - symbol
+   * - undefined
+   *
+   * Types may also be any of the following:
+   * - array
+   * - buffer
+   * - date
+   * - infinites
+   * - null
+   * - uuid
    * @param {Object} object
    * @param {String[]} properties
    * @param {String[]} types
@@ -114,6 +112,30 @@ class Database {
       this.logger.error(_scope, 'undefined argument', { object, properties, types });
       throw new DatabaseErrors.DataValidation();
     }
+
+    const supportedTypes = [
+      'array',
+      'bigint',
+      'boolean',
+      'buffer',
+      'date',
+      'function',
+      'infinites',
+      'null',
+      'number',
+      'object',
+      'string',
+      'symbol',
+      'undefined',
+      'uuid',
+    ];
+    types.forEach((t) => {
+      if (!supportedTypes.includes(t)) {
+        this.logger.error(_scope, 'unsupported type', { object, properties, types, unsupportedType: t });
+        throw new DatabaseErrors.DataValidation();
+      }
+    });
+
     properties.forEach((p) => {
       // eslint-disable-next-line security/detect-object-injection
       const pObj = object[p];
@@ -122,10 +144,10 @@ class Database {
       &&  !(types.includes('array') && Array.isArray(pObj))
       &&  !(types.includes('buffer') && pObj instanceof Buffer)
       &&  !(types.includes('date') && pObj instanceof Date)
-      &&  !(types.includes('infinites'))
+      &&  !(types.includes('infinites') && Math.abs(pObj) === Infinity)
       &&  !(types.includes('null') && pObj === null)
       &&  !(types.includes('number') && pType === 'bigint')
-      &&  !(types.includes('uuid') && Database._isUUID(pObj))) {
+      &&  !(types.includes('uuid') && uuid.validate(pObj))) {
         const reason = `'${p}' is '${pType}', but must be ${types.length > 1 ? 'one of ' : ''}'${types}'`;
         this.logger.error(_scope, reason, {});
         throw new DatabaseErrors.DataValidation(reason);
@@ -139,7 +161,7 @@ class Database {
    * @property {String} identifier
    * @property {String=} credential
    * @property {Date} created
-   * @property {Date=} lastAuthenticated
+   * @property {Date=} lastAuthentication
    */
   /**
    * @param {Authentication} authentication 
@@ -149,7 +171,7 @@ class Database {
       [['identifier'], ['string']],
       [['credential'], ['string', 'null']],
       [['created'], ['date']],
-      [['lastAuthenticated'], ['date', 'infinites']],
+      [['lastAuthentication'], ['date', 'infinites']],
     ].forEach(([properties, types]) => this._ensureTypes(authentication, properties, types));
   }
 
@@ -229,6 +251,17 @@ class Database {
   }
 
 
+  /**
+   * Insert or update an almanac entry.
+   * @param {*} dbCtx
+   * @param {String} event
+   * @param {Date=} date
+   */
+  async almanacUpsert(dbCtx, event, date) {
+    this._notImplemented('almanacUpsert', arguments);
+  }
+
+
   /**
    * Fetch the authentication record for an identifier.
    * @param {*} dbCtx
@@ -558,6 +591,44 @@ class Database {
     this._notImplemented('tokensGetByIdentifier', arguments);
   }
 
+
+  /** @typedef {Object} RedeemedTicketData
+   * @property {String} subject
+   * @property {String} resource
+   * @property {String=} iss
+   * @property {String} ticket
+   * @property {String} token
+   */
+  /**
+   * Persist details of a redeemed ticket.
+   * @param {*} dbCtx
+   * @param {RedeemedTicketData} redeemedData
+   * @returns {Promise<void>}
+   */
+  async ticketRedeemed(dbCtx, redeemedData) {
+    this._notImplemented('ticketRedeemed', arguments);
+  }
+
+
+  /**
+   * Update details of a redeemed ticket that it has been published.
+   * @param {*} dbCtx
+   * @param {RedeemedTicketData} redeemedData
+   * @returns {Promise<void>}
+   */
+  async ticketTokenPublished(dbCtx, redeemedData) {
+    this._notImplemented('ticketTokenPublished', arguments);
+  }
+
+  /**
+   * Retrieve redeemed tokens which have not yet been published to queue.
+   * @param {Number} limit
+   * @returns {Promise<RedeemedData[]>}
+   */
+  async ticketTokenGetUnpublished(dbCtx, limit) {
+    this._notImplemented('ticketTokenGetUnpublished', arguments);
+  }
+
 }
 
-module.exports = Database;
\ No newline at end of file
+module.exports = Database;