All files local-link.js

96.67% Statements 29/30
100% Branches 27/27
90.91% Functions 10/11
100% Lines 29/29
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126        2x             2x 31x                                                                                   19x   11x 5x   14x   14x 1x     13x 13x 13x 13x 13x             23x                 31x       31x 1x     30x             23x 19x 19x   18x 6x     12x 12x         12x       4x       2x      
import { ApolloLink, Observable } from 'apollo-link'
 
class LocalCacheLinkError extends Error {
  constructor (message) {
    super(`[LocalCacheLink] ${message}`)
  }
}
 
/**
 * Checks if the the storage is valid.
 */
const validStorage = storage =>
  storage &&
  storage.getItem &&
  storage.setItem &&
  storage.removeItem &&
  storage.clear
 
class LocalCacheLink extends ApolloLink {
  /**
   * @property {(boolean|Function)} [shouldCache] - The cache policy. When set
   *  to `true` defaults to cache all. When set to a callback, will receive the
   *  operation as argument, and should return `true` or `false`. Defaults to `true`.
   */
  shouldCache
 
  /**
   * @property {Function} [generateKey] - A callback to generate a key for
   *  an operation. Defaults to using `operation.toKey`.
   */
  generateKey
 
  /**
   * @property {(Object|Function)} [storage] - The Storage in use. Will
   *  default to window.localStorage when available.
   *
   * @see https://www.w3.org/TR/webstorage/#storage
   */
  storage
 
  /**
   * @property {Function} [normalize] - Normalization callback. Executed
   * prior to storing a query result.
   */
  normalize
 
  /**
   * @property {Function} [denormalize] - Denormalization callback. Executed
   * after retrieving a cached query result from the store.
   */
  denormalize
 
  constructor ({
    shouldCache = true,
    generateKey = operation => operation.toKey(),
    storage = typeof localStorage !== 'undefined' ? localStorage : null,
    normalize = data => JSON.stringify(data),
    denormalize = data => JSON.parse(data)
  } = {}) {
    super()
 
    if (!storage && typeof localStorage === 'undefined') {
      throw new LocalCacheLinkError('Could not determine a storage to use')
    }
 
    this.generateKey = generateKey
    this.shouldCache = shouldCache
    this.storage = storage
    this.normalize = normalize
    this.denormalize = denormalize
  }
 
  /**
   * Determines if an operation is cacheable or not.
   */
  isCacheable = operation =>
    typeof this.shouldCache === 'function'
      ? this.shouldCache(operation)
      : this.shouldCache
 
  /**
   * Retrieves the storage.
   */
  getStorage = operation => {
    const storage =
      typeof this.storage === 'function'
        ? this.storage(operation)
        : this.storage
 
    if (!validStorage(storage)) {
      throw new LocalCacheLinkError('Must provide a valid storage')
    }
 
    return storage
  }
 
  /**
   * Link query requester.
   */
  request = (operation, forward) => {
    if (this.isCacheable(operation)) {
      const key = this.generateKey(operation)
      const cached = this.getStorage(operation).getItem(key)
 
      if (cached) {
        return Observable.of(this.denormalize(cached, operation))
      }
 
      return forward(operation).map(result => {
        this.getStorage(operation).setItem(
          key,
          this.normalize(result, operation)
        )
 
        return result
      })
    }
 
    return forward(operation)
  }
}
 
const createLocalCacheLink = config => new LocalCacheLink(config)
 
export { LocalCacheLink, createLocalCacheLink }