X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;ds=sidebyside;f=src%2Fdb%2Fbase.js;h=205b8149778928a0ab915d83e9aaf64c158c35b8;hb=HEAD;hp=fbc2cbae0c2f83b2e79d5bc1a377ea5d8ca35a90;hpb=737fbd003d5c4dfea81b667ef906f1c106a60612;p=websub-hub diff --git a/src/db/base.js b/src/db/base.js index fbc2cba..8331fb2 100644 --- a/src/db/base.js +++ b/src/db/base.js @@ -12,9 +12,8 @@ const svh = require('./schema-version-helper'); const _fileScope = common.fileScope(__filename); class Database { - constructor(logger = common.nullLogger, options = {}) { + constructor(logger, options = {}) { this.logger = logger; - common.ensureLoggerLevels(this.logger); // Store the merged config and default values for lease values. // N.B. breaking hierarchy of config options here @@ -26,9 +25,9 @@ class Database { /** * Turn a snake into a camel. * Used when translating SQL column names to JS object style. - * @param {String} snakeCase - * @param {String|RegExp} delimiter - * @returns {String} + * @param {string} snakeCase snake case string + * @param {string | RegExp} delimiter default '_' + * @returns {string} camelCaseString */ static _camelfy(snakeCase, delimiter = '_') { if (!snakeCase || typeof snakeCase.split !== 'function') { @@ -44,9 +43,9 @@ class Database { /** * Basic type checking of object properties. - * @param {Object} object - * @param {String[]} properties - * @param {String[]} types + * @param {object} object object + * @param {string[]} properties list of property names + * @param {string[]} types list of valid types for property names */ _ensureTypes(object, properties, types) { const _scope = _fileScope('_ensureTypes'); @@ -73,8 +72,8 @@ class Database { /** * Interface methods need implementations. - * @param {String} method - * @param {arguments} args + * @param {string} method method name + * @param {arguments} args arguments */ _notImplemented(method, args) { this.logger.error(_fileScope(method), 'abstract method called', Array.from(args)); @@ -88,7 +87,8 @@ class Database { * At the minimum, this will validate a compatible schema is present and usable. * Some engines will also perform other initializations or async actions which * are easier handled outside the constructor. - */ + * @returns {Promise} + */ async initialize() { const _scope = _fileScope('initialize'); @@ -107,6 +107,7 @@ class Database { /** * Perform db connection healthcheck. + * @returns {Promise} */ async healthCheck() { this._notImplemented('healthCheck', arguments); @@ -115,8 +116,8 @@ class Database { /** * Replace any NULL from topic DB entry with default values. - * @param {Object} topic - * @returns {Object} + * @param {object} topic topic entry + * @returns {object} updated topic entry */ _topicDefaults(topic) { if (topic) { @@ -134,10 +135,10 @@ class Database { /** * Ensures any lease durations in data are consistent. - * @param {Object} data + * @param {object} data topic data */ _leaseDurationsValidate(data) { - const leaseProperties = Object.keys(this.topicLeaseDefaults) + const leaseProperties = Object.keys(this.topicLeaseDefaults); this._ensureTypes(data, leaseProperties, ['number', 'undefined', 'null']); // Populate defaults on a copy of values so we can check proper numerical ordering @@ -156,7 +157,7 @@ class Database { /** * Basic field validation for setting topic data. - * @param {Object} data + * @param {object} data topic data */ _topicSetDataValidate(data) { this._ensureTypes(data, ['url'], ['string']); @@ -167,18 +168,20 @@ class Database { /** * Basic field validation for setting topic content. - * @param {Object} data + * @param {object} data topic data */ _topicSetContentDataValidate(data) { this._ensureTypes(data, ['content'], ['string', 'buffer']); this._ensureTypes(data, ['contentHash'], ['string']); this._ensureTypes(data, ['contentType'], ['string', 'null', 'undefined']); + this._ensureTypes(data, ['eTag'], ['string', 'null', 'undefined']); + this._ensureTypes(data, ['lastModified'], ['string', 'null', 'undefined']); } /** * Basic field validation for updating topic. - * @param {Object} data + * @param {object} data topic data */ _topicUpdateDataValidate(data) { this._ensureTypes(data, ['publisherValidationUrl'], ['string', 'undefined', 'null']); @@ -199,7 +202,7 @@ class Database { /** * Basic field validation for setting verification data. - * @param {Object} data + * @param {object} data topic data */ _verificationDataValidate(data) { this._ensureTypes(data, ['topicId'], ['string', 'number']); @@ -212,7 +215,7 @@ class Database { /** * Basic field validation for updating verification data. - * @param {Object} verification + * @param {object} data verification data */ _verificationUpdateDataValidate(data) { this._ensureTypes(data, ['verificationId'], ['string', 'number']); @@ -224,7 +227,7 @@ class Database { /** * Basic field validation for upserting subscription data. - * @param {Object} subscription + * @param {object} data subscription data */ _subscriptionUpsertDataValidate(data) { this._ensureTypes(data, ['topicId'], ['string', 'number']); @@ -234,6 +237,10 @@ class Database { } + /** + * Basic field validation for subscription update data. + * @param {object} data subscription data + */ _subscriptionUpdateDataValidate(data) { this._ensureTypes(data, ['signatureAlgorithm'], ['string', 'null', 'undefined']); if (!common.validHash(data.signatureAlgorithm)) { @@ -244,26 +251,31 @@ class Database { /* Interface methods */ + /** + * @typedef {object} CommonDBInfo + * @property {number} changes result changes + * @property {*} lastInsertRowid result row id + * @property {number} duration result duration + */ /** * Normalize query information to a common form from a specific backend. - * @param {*} result - * @returns {Object} info - * @returns {Number} info.changes - * @returns {*} info.lastInsertRowid - * @returns {Number} info.duration + * @param {*} result db result */ _engineInfo(result) { this._notImplemented('engineInfo', arguments); } + /** + * @typedef {object} SchemaVersion + * @property {number} major semver major + * @property {number} minor semver minor + * @property {number} patch semver 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 {SchemaVersion} schema version */ async _currentSchema() { this._notImplemented('_currentSchema', arguments); @@ -281,7 +293,7 @@ class Database { /** * Wrap a function call in a transaction context. - * @param {*} dbCtx + * @param {*} dbCtx db context * @param {Function} fn fn(txCtx) */ async transaction(dbCtx, fn) { @@ -291,8 +303,8 @@ class Database { /** * Store an authentication success event. - * @param {*} dbCtx - * @param {String} identifier + * @param {*} dbCtx db context + * @param {string} identifier authentication identifier */ async authenticationSuccess(dbCtx, identifier) { this._notImplemented('authenticationSuccess', arguments); @@ -301,8 +313,8 @@ class Database { /** * Fetch authentication data for identifier. - * @param {*} dbCtx - * @param {*} identifier + * @param {*} dbCtx db context + * @param {*} identifier authentication identifier */ async authenticationGet(dbCtx, identifier) { this._notImplemented('authenticationGet', arguments); @@ -311,19 +323,42 @@ class Database { /** * Create or update an authentication entity. - * @param {*} dbCtx - * @param {String} identifier - * @param {String} credential + * @param {*} dbCtx db context + * @param {string} identifier authentication identifier + * @param {string} credential authentication credential + * @param {string=} otpKey authentication otp key */ - async authenticationUpsert(dbCtx, identifier, credential) { + async authenticationUpsert(dbCtx, identifier, credential, otpKey) { this._notImplemented('authenticationUpsert', arguments); } + /** + * Update an authentication entity's otp key. + * @param {*} dbCtx db context + * @param {string} identifier authentication identifier + * @param {string=} otpKey authentication otp key + */ + async authenticationUpdateOTPKey(dbCtx, identifier, otpKey) { + this._notImplemented('authenticationUpdateKey', arguments); + } + + + /** + * Update an authentication entity's credential. + * @param {*} dbCtx db context + * @param {string} identifier authentication identifier + * @param {string} credential authentication credential + */ + async authenticationUpdateCredential(dbCtx, identifier, credential) { + this._notImplemented('authenticationUpdateKey', arguments); + } + + /** * All subscriptions to a topic. - * @param {*} dbCtx - * @param {String} topicId + * @param {*} dbCtx db context + * @param {string} topicId topic id */ async subscriptionsByTopicId(dbCtx, topicId) { this._notImplemented('subscriptionsByTopicId', arguments); @@ -332,8 +367,8 @@ class Database { /** * Number of subscriptions to a topic. - * @param {*} dbCtx - * @param {String} topicUrl + * @param {*} dbCtx db context + * @param {string} topicUrl topic url */ async subscriptionCountByTopicUrl(dbCtx, topicUrl) { this._notImplemented('subscriptionCountByTopicUrl', arguments); @@ -342,9 +377,9 @@ class Database { /** * Remove an existing subscription. - * @param {*} dbCtx - * @param {String} callback - * @param {*} topicId + * @param {*} dbCtx db context + * @param {string} callback subscriber callback url + * @param {*} topicId topic id */ async subscriptionDelete(dbCtx, callback, topicId) { this._notImplemented('subscriptionDelete', arguments); @@ -353,20 +388,23 @@ class Database { /** * Remove any expired subscriptions to a topic. - * @param {*} dbCtx - * @param {*} topicId + * @param {*} dbCtx db context + * @param {*} topicId topic id */ async subscriptionDeleteExpired(dbCtx, topicId) { this._notImplemented('subscriptionDeleteExpired', arguments); } + /** + * @alias {number} Integer + */ /** * Claim subscriptions needing content updates attempted. - * @param {*} dbCtx - * @param {Number} wanted maximum subscription updates to claim + * @param {*} dbCtx db context + * @param {number} wanted maximum subscription updates to claim * @param {Integer} claimTimeoutSeconds age of claimed updates to reclaim - * @param {String} claimant + * @param {string} claimant worker claiming processing * @returns {Array} list of subscriptions */ async subscriptionDeliveryClaim(dbCtx, wanted, claimTimeoutSeconds, claimant) { @@ -376,10 +414,10 @@ class Database { /** * Claim a subscription delivery. - * @param {*} dbCtx - * @param {*} subscriptionId - * @param {*} claimTimeoutSeconds - * @param {*} claimant + * @param {*} dbCtx db context + * @param {*} subscriptionId subscription id + * @param {number} claimTimeoutSeconds duration of claim + * @param {*} claimant worker claiming processing */ async subscriptionDeliveryClaimById(dbCtx, subscriptionId, claimTimeoutSeconds, claimant) { this._notImplemented('subscriptionDeliveryClaimById', arguments); @@ -388,9 +426,9 @@ class Database { /** * A subscriber successfully received new topic content, update subscription. - * @param {*} dbCtx - * @param {String} callback - * @param {*} topicId + * @param {*} dbCtx db context + * @param {string} callback subscriber callback url + * @param {*} topicId topic id */ async subscriptionDeliveryComplete(dbCtx, callback, topicId) { this._notImplemented('subscriptionDeliveryComplete', arguments); @@ -399,9 +437,9 @@ class Database { /** * A subscriber denied new topic content, remove subscription. - * @param {*} dbCtx - * @param {String} callback - * @param {*} topicId + * @param {*} dbCtx db context + * @param {string} callback subscriber callback url + * @param {*} topicId topic id */ async subscriptionDeliveryGone(dbCtx, callback, topicId) { this._notImplemented('subscriptionDeliveryGone', arguments); @@ -410,10 +448,10 @@ class Database { /** * An attempt to deliver content to a subscriber did not complete, update delivery accordingly. - * @param {*} dbCtx - * @param {String} callback - * @param {*} topicId - * @param {Number[]} retryDelays + * @param {*} dbCtx db context + * @param {string} callback subscriber callback url + * @param {*} topicId topic id + * @param {number[]} retryDelays list of retry delays */ async subscriptionDeliveryIncomplete(dbCtx, callback, topicId, retryDelays) { this._notImplemented('subscriptionDeliveryIncomplete', arguments); @@ -422,9 +460,9 @@ class Database { /** * Fetch subscription details - * @param {*} dbCtx - * @param {String} callback - * @param {*} topicId + * @param {*} dbCtx db context + * @param {string} callback subscriber callback url + * @param {*} topicId topic id */ async subscriptionGet(dbCtx, callback, topicId) { this._notImplemented('subscriptionGet', arguments); @@ -433,8 +471,8 @@ class Database { /** * Fetch subscription details - * @param {*} dbCtx - * @param {*} subscriptionId + * @param {*} dbCtx db context + * @param {*} subscriptionId subscription id */ async subscriptionGetById(dbCtx, subscriptionId) { this._notImplemented('subscriptionGetById', arguments); @@ -443,14 +481,14 @@ class Database { /** * Set subscription details - * @param {*} dbCtx - * @param {Object} data - * @param {String} data.callback - * @param {*} data.topicId - * @param {Number} data.leaseSeconds - * @param {String=} data.secret - * @param {String=} data.httpRemoteAddr - * @param {String=} data.httpFrom + * @param {*} dbCtx db context + * @param {object} data subscription data + * @param {string} data.callback subscriber callback url + * @param {*} data.topicId topic id + * @param {number} data.leaseSeconds lease seconds + * @param {string=} data.secret secret + * @param {string=} data.httpRemoteAddr subscriber info + * @param {string=} data.httpFrom subscriber info */ async subscriptionUpsert(dbCtx, data) { this._notImplemented('subscriptionUpsert', arguments); @@ -459,10 +497,10 @@ class Database { /** * Set some subscription fields - * @param {*} dbCtx - * @param {Object} data - * @param {*} data.subscriptionId - * @param {String} data.signatureAlgorithm + * @param {*} dbCtx db context + * @param {object} data subscription data + * @param {*} data.subscriptionId subscription id + * @param {string} data.signatureAlgorithm signature algorithm */ async subscriptionUpdate(dbCtx, data) { this._notImplemented('subscriptionUpdate', arguments); @@ -471,8 +509,8 @@ class Database { /** * Sets the isDeleted flag on a topic, and reset update time. - * @param {*} txCtx - * @param {*} topicId + * @param {*} dbCtx db context + * @param {*} topicId topic id */ async topicDeleted(dbCtx, topicId) { this._notImplemented('topicDeleted', arguments); @@ -481,10 +519,10 @@ class Database { /** * Claim topics to fetch updates for, from available. - * @param {*} dbCtx + * @param {*} dbCtx db context * @param {Integer} wanted maximum topic fetches to claim * @param {Integer} claimTimeoutSeconds age of claimed topics to reclaim - * @param {String} claimant node id claiming these fetches + * @param {string} claimant node id claiming these fetches */ async topicFetchClaim(dbCtx, wanted, claimTimeoutSeconds, claimant) { this._notImplemented('topicFetchClaim', arguments); @@ -493,10 +531,10 @@ class Database { /** * Claim a topic to update. - * @param {*} dbCtx - * @param {*} topicId + * @param {*} dbCtx db context + * @param {*} topicId topic id * @param {Integer} claimTimeoutSeconds age of claimed topics to reclaim - * @param {String} claimant node id claiming these fetches + * @param {string} claimant node id claiming these fetches */ async topicFetchClaimById(dbCtx, topicId, claimTimeoutSeconds, claimant) { this._notImplemented('topicFetchClaim', arguments); @@ -505,8 +543,8 @@ class Database { /** * Reset publish state, and reset deliveries for subscribers. - * @param {*} dbCtx - * @param {*} topicId + * @param {*} dbCtx db context + * @param {*} topicId topic id */ async topicFetchComplete(dbCtx, topicId) { this._notImplemented('topicFetchComplete', arguments); @@ -515,9 +553,9 @@ class Database { /** * Bump count of attempts and release claim on update. - * @param {*} dbCtx - * @param {*} topicId - * @param {Number[]} retryDelays + * @param {*} dbCtx db context + * @param {*} topicId topic id + * @param {number[]} retryDelays retry delays */ async topicFetchIncomplete(dbCtx, topicId, retryDelays) { this._notImplemented('topicFetchIncomplete', arguments); @@ -526,9 +564,8 @@ class Database { /** * Set a topic as ready to be checked for an update. - * @param {*} dbCtx - * @param {*} topicId - * @returns {Boolean} + * @param {*} dbCtx db context + * @param {*} topicId topic id */ async topicFetchRequested(dbCtx, topicId) { this._notImplemented('topicPublish', arguments); @@ -537,7 +574,7 @@ class Database { /** * Get all data for all topics, including subscription count. - * @param {*} dbCtx + * @param {*} dbCtx db context */ async topicGetAll(dbCtx) { this._notImplemented('topicGetAll', arguments); @@ -546,19 +583,20 @@ class Database { /** * Get topic data, without content. - * @param {*} dbCtx - * @param {String} topicUrl + * @param {*} dbCtx db context + * @param {string} topicUrl topic url + * @param {boolean} applyDefaults merge defaults into result */ - async topicGetByUrl(dbCtx, topicUrl) { + async topicGetByUrl(dbCtx, topicUrl, applyDefaults = true) { this._notImplemented('topicGetByUrl', arguments); } /** * Get topic data, without content. - * @param {*} dbCtx - * @param {*} topicId - * @param {Boolean} applyDefaults + * @param {*} dbCtx db context + * @param {*} topicId topic id + * @param {boolean} applyDefaults merge defaults into result */ async topicGetById(dbCtx, topicId, applyDefaults = true) { this._notImplemented('topicGetById', arguments); @@ -567,10 +605,10 @@ class Database { /** * Returns topic data with content. - * @param {*} dbCx - * @param {*} topicId + * @param {*} dbCtx db context + * @param {*} topicId topic id */ - async topicGetContentById(dbCx, topicId) { + async topicGetContentById(dbCtx, topicId) { this._notImplemented('topicGetContentById', arguments); } @@ -578,7 +616,8 @@ class Database { /** * Attempt to delete a topic, which must be set isDeleted, if there * are no more subscriptions belaying its removal. - * @param {*} topicId + * @param {*} dbCtx db context + * @param {*} topicId topic id */ async topicPendingDelete(dbCtx, topicId) { this._notImplemented('topicPendingDelete', arguments); @@ -587,19 +626,23 @@ class Database { /** * Return an array of the counts of the last #days of topic updates. - * @param {*} dbCtx - * @param {*} topicId - * @param {Number} days + * @param {*} dbCtx db context + * @param {*} topicId topic id + * @param {number} days days back to count + * @returns {number[]} updates in last days */ async topicPublishHistory(dbCtx, topicId, days) { this._notImplemented('topicPublishHistory', arguments); } + /** + * @alias {object} TopicData + */ /** * Create or update the basic parameters of a topic. - * @param {*} dbCtx - * @param {TopicData} data + * @param {*} dbCtx db context + * @param {TopicData} data topic data */ async topicSet(dbCtx, data) { this._notImplemented('topicSet', arguments); @@ -608,11 +651,14 @@ class Database { /** * Updates a topic's content data and content update timestamp. - * @param {Object} data - * @param {*} data.topicId - * @param {String} data.content - * @param {String} data.contentHash - * @param {String=} data.contentType + * @param {*} dbCtx db context + * @param {object} data topic data + * @param {*} data.topicId topic id + * @param {string} data.content content + * @param {string} data.contentHash content hash + * @param {string=} data.contentType content-type + * @param {string=} data.eTag etag header + * @param {string=} data.lastModified last modified header */ async topicSetContent(dbCtx, data) { this._notImplemented('topicSetContent', arguments); @@ -621,25 +667,29 @@ class Database { /** * Set some topic fields. - * @param {*} dbCtx - * @param {Object} data - * @param {*} data.topicId - * @param {Number=} data.leaseSecondsPreferred - * @param {Number=} data.leaseSecondsMin - * @param {Number=} data.leaseSecondsMax - * @param {String=} data.publisherValidationUrl - * @param {String=} data.contentHashAlgorithm + * @param {*} dbCtx db context + * @param {object} data topic data + * @param {*} data.topicId topic id + * @param {number=} data.leaseSecondsPreferred preferred topic lease seconds + * @param {number=} data.leaseSecondsMin min lease seconds + * @param {number=} data.leaseSecondsMax max lease seconds + * @param {string=} data.publisherValidationUrl publisher validation url + * @param {string=} data.contentHashAlgorithm content hash algorithm */ async topicUpdate(dbCtx, data) { this._notImplemented('topicUpdate', arguments); } + /** + * @alias {object} Verification + */ /** * Claim pending verifications for attempted resolution. - * @param {*} dbCtx + * @param {*} dbCtx db context * @param {Integer} wanted maximum verifications to claim * @param {Integer} claimTimeoutSeconds age of claimed verifications to reclaim + * @param {*} claimant worker claiming processing * @returns {Verification[]} array of claimed verifications */ async verificationClaim(dbCtx, wanted, claimTimeoutSeconds, claimant) { @@ -649,10 +699,10 @@ class Database { /** * Claim a specific verification by id, if no other similar verification claimed. - * @param {*} dbCtx - * @param {*} verificationId - * @param {Number} claimTimeoutSeconds - * @param {String} claimant + * @param {*} dbCtx db context + * @param {*} verificationId verification id + * @param {number} claimTimeoutSeconds claim duration + * @param {string} claimant worker claiming processing */ async verificationClaimById(dbCtx, verificationId, claimTimeoutSeconds, claimant) { this._notImplemented('verificationClaimById', arguments); @@ -660,12 +710,12 @@ class Database { /** - * Remove the verification, any older - * verifications for that same client/topic, and the claim. - * @param {*} dbCtx - * @param {*} verificationId - * @param {String} callback - * @param {*} topicId + * Remove the verification, any older verifications for that same client/topic, + * and remove the claim. + * @param {*} dbCtx db context + * @param {*} verificationId verification id + * @param {string} callback subscriber callback url + * @param {*} topicId topic id */ async verificationComplete(dbCtx, verificationId, callback, topicId) { this._notImplemented('verificationComplete', arguments); @@ -674,8 +724,8 @@ class Database { /** * Get verification data. - * @param {*} dbCtx - * @param {*} verificationId + * @param {*} dbCtx db context + * @param {*} verificationId verification id */ async verificationGetById(dbCtx, verificationId) { this._notImplemented('verificationGetById', arguments); @@ -685,21 +735,22 @@ class Database { /** * Update database that a client verification was unable to complete. * This releases the delivery claim and reschedules for some future time. - * @param {*} dbCtx - * @param {String} callback client callback url - * @param {*} topicId internal topic id - * @param {Number[]} retryDelays + * @param {*} dbCtx db context + * @param {*} verificationId verification id + * @param {number[]} retryDelays retry delays */ async verificationIncomplete(dbCtx, verificationId, retryDelays) { this._notImplemented('verificationIncomplete', arguments); } + /** + * @alias {object} VerificationData + */ /** * Create a new pending verification. - * @param {*} dbCtx - * @param {VerificationData} data - * @param {Boolean} claim + * @param {*} dbCtx db context + * @param {VerificationData} verification verification data * @returns {*} verificationId */ async verificationInsert(dbCtx, verification) { @@ -709,9 +760,8 @@ class Database { /** * Relinquish the claim on a verification, without any other updates. - * @param {*} dbCtx - * @param {String} callback client callback url - * @param {*} topicId internal topic id + * @param {*} dbCtx db context + * @param {*} verificationId verification id */ async verificationRelease(dbCtx, verificationId) { this._notImplemented('verificationRelease', arguments); @@ -720,12 +770,12 @@ class Database { /** * Updates some fields of an existing (presumably claimed) verification. - * @param {*} dbCtx - * @param {*} verificationId - * @param {Object} data - * @param {String} data.mode - * @param {String} data.reason - * @param {Boolean} data.isPublisherValidated + * @param {*} dbCtx db context + * @param {*} verificationId verification id + * @param {object} data verification data + * @param {string} data.mode mode + * @param {string} data.reason reason + * @param {boolean} data.isPublisherValidated publisher validation result */ async verificationUpdate(dbCtx, verificationId, data) { this._notImplemented('verificationUpdate', arguments); @@ -734,8 +784,8 @@ class Database { /** * Sets the isPublisherValidated flag on a verification and resets the delivery - * @param {*} dbCtx - * @param {*} verificationId + * @param {*} dbCtx db context + * @param {*} verificationId verification id */ async verificationValidated(dbCtx, verificationId) { this._notImplemented('verificationValidated', arguments);