mozilla/publish.webmaker.org

View on GitHub
api/classes/base_export_cache.js

Summary

Maintainability
A
3 hrs
Test Coverage
"use strict";

const Hoek = require(`hoek`);

const BaseCache = require(`./base_cache`);
const Tokenizer = require(`../../lib/utils`).Tokenizer;

class ExportCache extends BaseCache {
  constructor(server, resourceName, keyPrefix, cacheExpirySec=30*60) {
    Hoek.assert(resourceName, `A resource name must be provided when extending the ExportCache class.`);
    Hoek.assert(keyPrefix, `A prefix for keys in the cache must be provided when extending the ExportCache class.`);

    super(server);
    this.resourceName = resourceName;
    this.keyPrefix = keyPrefix;
    this.cacheExpirySec = cacheExpirySec;
  }

  get config() {
    // We want to override the base config since we want to implement our own
    // caching process
    return null;
  }

  /*
   * This method takes an object `resourceIdentifier` as an argument
   * If the `token` property is set in the object, it verifies that an
   * `id` exists for the token and returns it, otherwise throws an error
   * If the `id` property is set in the object, it generates a token for the
   * resource corresponding to the `id`, and stores it in cache
   * If neither property is present in the object, it throws an error
   */
  run(resourceIdentifier, next) {
    /*
     * Validating an existing token for a resource
     */
    if (!resourceIdentifier) {
      return next(new Error(`Fatal Error: No resource identifier was passed in.`));
    }

    const { id, token } = resourceIdentifier;

    if (typeof token === `string`) {
      if (!this.cache) {
        return next(null, token);
      }

      let saltedToken = ``;

      try {
        saltedToken = Tokenizer.salt(token);
      } catch(err) {
        return next(err);
      }

      return this.cache.get(`${this.keyPrefix}:${saltedToken}`)
      .then(cachedId => {
        if (!cachedId) {
          return next(`Token ${saltedToken} provided for ${this.resourceName} is invalid`);
        }

        return next(null, parseInt(cachedId));
      })
      .catch(next);
    }

    if (typeof id !== `number`) {
      return next(new Error(`Fatal Error: Resource identifier does not contain an id or token.`));
    }

    /*
     * Generating a token for a resource
     */
    if (!this.cache) {
      return next(null, id);
    }

    return Tokenizer.generate((err, generatedToken) => {
      if (err) {
        return next(err);
      }

      this.cache.setex(
        `${this.keyPrefix}:${generatedToken.salted}`,
        this.cacheExpirySec,
        id
      )
      .then(() => next(null, generatedToken.value))
      .catch(next);
    });
  }

  drop(token, next) {
    if (!this.cache) {
      return next();
    }

    const saltedToken = Tokenizer.salt(token);

    return this.cache.del(`${this.keyPrefix}:${saltedToken}`)
    .then(() => next())
    .catch(next);
  }
}

module.exports = ExportCache;