import { isUndefined, has } from "lodash" import { take } from "lodash/fp" import { Readable, Writable } from "readable-stream" import { Buffer } from "safe-buffer" import { splitKey, joinKey, $, keySep, getFileFromKey } from "../src/common" import { getLastPartInKey } from "../src/templateApi/hierarchy" const folderMarker = "OH-YES-ITSA-FOLDER-" const isFolder = val => { if (isUndefined(val)) { throw new Error("Passed undefined value for folder") } return val.includes(folderMarker) } const getParentFolderKey = key => $(key, [splitKey, take(splitKey(key).length - 1), joinKey]) const getParentFolder = (data, key) => { if (key === keySep) return null const parentKey = getParentFolderKey(key) if (parentKey === keySep) return null if (data[parentKey] === undefined) throw new Error( "Parent folder for " + key + " does not exist (" + parentKey + ")" ) return JSON.parse(data[parentKey]) } const addItemToParentFolder = (data, path) => { if (getParentFolderKey(path) === "/") return const parentFolder = getParentFolder(data, path) parentFolder.items.push(getLastPartInKey(path)) data[getParentFolderKey(path)] = JSON.stringify(parentFolder) } export const createFile = data => async (path, content) => { if (await exists(data)(path)) { throw new Error(path + " already exists") } addItemToParentFolder(data, path) data[path] = content } export const updateFile = data => async (path, content) => { // putting this check in to force use of create if (!(await exists(data)(path))) { throw new Error("cannot update " + path + " - does not exist") } data[path] = content } export const writableFileStream = data => async path => { //if(!await exists(data)(path)) throw new Error("cannot write stream to " + path + " - does not exist"); if (!getParentFolder(data, path)) { throw new Error("Parent folder for " + path + " does not exist") } const stream = Writable() stream._write = (chunk, encoding, done) => { data[path] = data[path] === undefined ? [] : data[path] data[path] = [...data[path], ...chunk] done() } addItemToParentFolder(data, path) return stream } export const readableFileStream = data => async path => { if (!(await exists(data)(path))) throw new Error("cannot read stream from " + path + " - does not exist") const s = new Readable() s._read = () => { s.push(Buffer.from(data[path])) s.push(null) } return s } export const getFileSize = data => async path => { if (!(await exists(data)(path))) throw new Error("cannot get size of " + path + " - does not exist") return data[path].length } export const renameFile = data => async (oldKey, newKey) => { if (!(await exists(data)(oldKey))) throw new Error("cannot rename path: " + oldKey + " ... does not exist") if (await exists(data)(newKey)) throw new Error("cannot rename path: " + newKey + " ... already exists") data[newKey] = data[oldKey] delete data[oldKey] const parent = getParentFolder(data, newKey) const oldFileName = getFileFromKey(oldKey) const newFileName = getFileFromKey(newKey) parent.items = [...parent.items.filter(i => i !== oldFileName), newFileName] data[getParentFolderKey(newKey)] = JSON.stringify(parent) } export const loadFile = data => async path => { const result = data[path] if (isUndefined(result)) { throw new Error("Load failed - path " + path + " does not exist") } return result } export const exists = data => async path => has(data, path) export const deleteFile = data => async path => { if (!(await exists(data)(path))) throw new Error("Cannot delete file, path " + path + " does not exist") if (isFolder(data[path])) throw new Error("DeleteFile: Path " + path + " is a folder, not a file") const parentFolder = getParentFolder(data, path) parentFolder.items = parentFolder.items.filter( i => i !== getLastPartInKey(path) ) data[getParentFolderKey(path)] = JSON.stringify(parentFolder) delete data[path] } export const createFolder = data => async path => { if (await exists(data)(path)) throw new Error("Cannot create folder, path " + path + " already exists") addItemToParentFolder(data, path) data[path] = JSON.stringify({ folderMarker, items: [] }) } export const deleteFolder = data => async path => { if (!(await exists(data)(path))) throw new Error("Cannot delete folder, path " + path + " does not exist") if (!isFolder(data[path])) throw new Error("DeleteFolder: Path " + path + " is not a folder") for (let item of JSON.parse(data[path]).items) { const fullItemPath = `${path}/${item}` if (isFolder(data[fullItemPath])) { await deleteFolder(data)(fullItemPath) } else { await deleteFile(data)(fullItemPath) } } const parent = getParentFolder(data, path) if (parent) { parent.items = parent.items.filter(f => f !== getLastPartInKey(path)) data[getParentFolderKey(path)] = JSON.stringify(parent) } delete data[path] } export const getFolderContents = data => async folderPath => { if (!(await exists(data)(folderPath))) throw new Error("Folder does not exist: " + folderPath) if (!isFolder(data[folderPath])) throw new Error("Not a folder: " + folderPath) return JSON.parse(data[folderPath]).items } export default data => { return { createFile: createFile(data), updateFile: updateFile(data), loadFile: loadFile(data), exists: exists(data), deleteFile: deleteFile(data), createFolder: createFolder(data), deleteFolder: deleteFolder(data), readableFileStream: readableFileStream(data), writableFileStream: writableFileStream(data), renameFile: renameFile(data), getFolderContents: getFolderContents(data), getFileSize: getFileSize(data), datastoreType: "memory", datastoreDescription: "", data, } }