UNPKG

@amag-ch/sap_cap_common_objectstore

Version:

NodeJS library to communicate with an objectstore

132 lines (104 loc) 3.75 kB
const path = require('node:path') const fs = require('node:fs/promises') const { createReadStream } = require('node:fs') const Client = require('./Client') const { ObjectStoreError, ObjectNotFound, ObjectStoreCommunicationError } = require('../errors') module.exports = class LocalClient extends Client { #folderName constructor(credentials={}) { super(credentials) const { folder = '~/.cds-objectstore' } = credentials this.#folderName = path.resolve(folder.replace(/^~/, () => require('os').userInfo().homedir)) } /** * Initialise. * * @returns {Promise<void>} */ init = async () => { await fs.mkdir(this.#folderName, { recursive: true }) return super.init() } /** * List all files in store. * * @returns {Promise<[{filename: string, modifiedAt: Date, contentLength: integer}]>} */ list = async () => { const entries = await fs.readdir(this.#folderName, { withFileTypes: true }) const result = await Promise.all(entries.filter(entry => entry.isFile()).map(({ name: filename }) => fs.stat(this.#getAbsoluteName(filename)).then(stats => ({ filename, modifiedAt: stats.mtime, contentLength: stats.size })) )) return result } /** * Read metadata from file. * * @param {string} filename * @returns {Promise<{modifiedAt: Date, contentLength: integer}>} */ head = async (filename) => { if (!filename) throw new Error('filename not provided') try { const stats = await fs.stat(this.#getAbsoluteName(filename)) if (!stats.isFile()) throw new ObjectNotFound(`File with name ${filename} not exists`) await fs.access(this.#getAbsoluteName(filename), fs.constants.F_OK | fs.constants.R_OK ) return { modifiedAt: stats.mtime, contentLength: stats.size } } catch (err) { if (err.code === 'ENOENT') throw new ObjectNotFound(`File with name ${filename} not exists`) if (err.code === 'EACCES') throw new ObjectStoreCommunicationError(`File with name ${filename} is not readable`) throw new ObjectStoreError(err.message, { cause: err }) } } /** * Read file. * * @param {string} filename * @returns {Promise<ReadableStream>} */ get = async (filename) => { if (!filename) throw new Error('filename not provided') await this.head(filename) return createReadStream(this.#getAbsoluteName(filename)) } /** * Put new file to store. * * @param {string} filename * @param {string|Readable|Blob|ReadableStream<any>|Uint8Array|Buffer} content * @returns {Promise<void>} */ put = async (filename, content) => { if (!filename) throw new Error('filename not provided') if (!content) throw new Error('content not provided') await fs.writeFile(this.#getAbsoluteName(filename), content) } /** * Delete file in store. * * @param {string} filename * @returns {Promise<void>} */ delete = async (filename) => { if (!filename) throw new Error('filename not provided') try { await this.head(filename) await fs.rm(this.#getAbsoluteName(filename)) } catch (err) { if (err instanceof ObjectNotFound) return throw new ObjectStoreError(err.message, { cause: err }) } } #getAbsoluteName = (filename) => `${this.#folderName}/${filename}` }