All files / lib/plugins/stores file-store.ts

86.79% Statements 46/53
61.11% Branches 11/18
90% Functions 9/10
86.05% Lines 37/43

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 127 128 129 130 131 132 133 134 135    1x 1x 1x     1x 1x                             6x 6x                               2x                   1x         1x 1x 1x     1x 1x 2x     1x                   1x 1x 1x 1x                     1x 1x 1x 1x 2x 1x                   1x 1x 1x       1x 1x 2x 1x               1x 2x   1x   1x                
"use strict";
 
import fs from "fs";
import { promisify } from "util";
import { MemoryStore, PropertyMeta } from "./memory-store";
import { Store } from "./types";
 
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
 
export type FileStoreArgs = {
  path: string;
  data?: { [key: string]: unknown };
};
 
/**
 * read an object from a JSON file
 *
 * @param {String} file - the path to a JSON file
 * @returns {Object|void} the parsed JSON object or void if the file does not exist
 */
async function readJsonFile(file: string) {
  try {
    const buffer = await readFile(file);
    return JSON.parse(buffer.toString());
  } catch (error) {
    if (error.code !== "ENOENT") {
      throw error;
    }
  }
}
 
/**
 * write an object to a file as JSON
 *
 * @param {String} file - the path to a JSON file
 * @param {Object} content - plain Object
 * @returns {Promise} the result from writeFile
 */
function writeJsonFile(file: string, content: { [key: string]: unknown }) {
  return writeFile(file, JSON.stringify(content), "utf8");
}
 
/**
 * get a FileStore instance
 *
 * @param {Object} args -
 * @param {String} args.file - the path to a JSON file
 * @returns {Object} FileStore
 */
export class FileStore implements Store {
  filePath: string;
  initData: { [key: string]: unknown };
 
  constructor(args: FileStoreArgs) {
    const { path, data } = args;
    this.filePath = path;
    this.initData = data || {};
  }
 
  async load() {
    const data = await readJsonFile(this.filePath);
    if (E!data) {
      await writeJsonFile(this.filePath, this.initData);
    }
    return this;
  }
 
  /**
   * get a property from the store
   *
   * @param {Object} prop - property metadata
   * @param {String} prop.propertyPath - the path to the property
   * @returns {Promise} property value
   */
  async get(prop: PropertyMeta) {
    const data = (await readJsonFile(this.filePath)) || {};
    const memStore = new MemoryStore({ data });
    return memStore.get(prop);
  }
 
  /**
   * set a property in the store
   *
   * @param {Object} prop - property metadata
   * @param {String} prop.propertyPath - the path to the property
   * @param {*} value - The value to set on the property.
   * @returns {Promise} property value
   */
  async set(prop: PropertyMeta, value: unknown) {
    const data = (await readJsonFile(this.filePath)) || {};
    const memStore = new MemoryStore({ data });
    const newValue = await memStore.set(prop, value);
    await writeJsonFile(this.filePath, memStore.read());
    return newValue;
  }
 
  /**
   * delete a property from the store
   *
   * @param {Object} prop - property metadata
   * @param {String} prop.propertyPath - the path to the property
   * @returns {Promise} previous property value
   */
  async delete(prop: PropertyMeta) {
    const data = await readJsonFile(this.filePath);
    Iif (!data) {
      // file does not exist
      return;
    }
    const memStore = new MemoryStore({ data });
    const value = await memStore.delete(prop);
    await writeJsonFile(this.filePath, memStore.read());
    return value;
  }
 
  /**
   * Read file contents
   *
   * @returns {Promise} the value of the property
   */
  async read() {
    return readJsonFile(this.filePath);
  }
}
 
export async function plugin(args: FileStoreArgs) {
  const store = await new FileStore(args).load();
  return {
    stores: {
      file: store
    }
  };
}