Skip to content

Commit

Permalink
Abstracted away req/res from VFS calls in favor of options (#34)
Browse files Browse the repository at this point in the history
Instead of:

```
const adapter = core => ({
  readdir: vfs => file => {
    console.log(vfs.req.session.user.username)
  }
})
```

Is now this:

```
const adapter = core => ({
  readdir: vfs => (file, options) => {
    console.log(options.session.user.username)
  }
})
```
  • Loading branch information
andersevenrud committed Jul 22, 2020
1 parent 41f1cad commit de77937
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 67 deletions.
53 changes: 26 additions & 27 deletions __tests__/adapters/vfs/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,6 @@ describe('VFS System adapter', () => {
afterAll(() => core.destroy());

const vfs = {
req: {
session: {
user: {
username: 'jest'
}
}
},
res: {

},
mount: {
name: 'home',
root: 'home:/',
Expand All @@ -34,18 +24,27 @@ describe('VFS System adapter', () => {
}
};

const createOptions = (options = {}) => ({
...options,
session: {
user: {
username: 'jest'
}
}
});

const request = (name, ...args) => adapter[name](vfs, vfs)(...args);

test('#touch', () => {
return expect(request('touch', 'home:/test'))
return expect(request('touch', 'home:/test', createOptions()))
.resolves
.toBe(true);
});

test('#stat', () => {
const realPath = path.join(core.configuration.tempPath, 'jest/test');

return expect(request('stat', 'home:/test'))
return expect(request('stat', 'home:/test', createOptions()))
.resolves
.toMatchObject({
filename: 'test',
Expand All @@ -58,37 +57,37 @@ describe('VFS System adapter', () => {
});

test('#copy', () => {
return expect(request('copy', 'home:/test', 'home:/test-copy'))
return expect(request('copy', 'home:/test', 'home:/test-copy', createOptions()))
.resolves
.toBe(true);
});

test('#rename', () => {
return expect(request('rename', 'home:/test-copy', 'home:/test-rename'))
return expect(request('rename', 'home:/test-copy', 'home:/test-rename', createOptions()))
.resolves
.toBe(true);
});

test('#mkdir', () => {
return expect(request('mkdir', 'home:/test-directory'))
return expect(request('mkdir', 'home:/test-directory', createOptions()))
.resolves
.toBe(true);
});

test('#mkdir - existing directory', () => {
return expect(request('mkdir', 'home:/test-directory'))
return expect(request('mkdir', 'home:/test-directory', createOptions()))
.rejects
.toThrowError();
});

test('#mkdir - ensure', () => {
return expect(request('mkdir', 'home:/test-directory', {ensure: true}))
return expect(request('mkdir', 'home:/test-directory', createOptions({ensure: true})))
.resolves
.toBe(true);
});

test('#readfile', () => {
return expect(request('readfile', 'home:/test'))
return expect(request('readfile', 'home:/test', createOptions()))
.resolves
.toBeInstanceOf(stream.Readable);
});
Expand All @@ -99,31 +98,31 @@ describe('VFS System adapter', () => {
s.push('jest');
s.push(null);

return expect(request('writefile', 'home:/test', s))
return expect(request('writefile', 'home:/test', s, createOptions()))
.resolves
.toBe(true);
});

test('#exists - existing file', () => {
return expect(request('exists', 'home:/test-rename'))
return expect(request('exists', 'home:/test-rename', createOptions()))
.resolves
.toBe(true);
});

test('#exists - existing directory', () => {
return expect(request('exists', 'home:/test-directory'))
return expect(request('exists', 'home:/test-directory', createOptions()))
.resolves
.toBe(true);
});

test('#exists - non existing file', () => {
return expect(request('exists', 'home:/test-copy'))
return expect(request('exists', 'home:/test-copy', createOptions()))
.resolves
.toBe(false);
});

test('#search', () => {
return expect(request('search', 'home:/', '*'))
return expect(request('search', 'home:/', '*', createOptions()))
.resolves
.toEqual(
expect.arrayContaining([
Expand All @@ -140,7 +139,7 @@ describe('VFS System adapter', () => {
});

test('#readdir', () => {
return expect(request('readdir', 'home:/'))
return expect(request('readdir', 'home:/', createOptions()))
.resolves
.toEqual(
expect.arrayContaining([
Expand All @@ -164,22 +163,22 @@ describe('VFS System adapter', () => {
const files = ['home:/test', 'home:/test-directory', 'home:/test-rename'];

return Promise.all(files.map(f => {
return expect(request('unlink', f))
return expect(request('unlink', f, createOptions()))
.resolves
.toBe(true);
}));
});

test('#unlink', () => {
return expect(request('unlink', 'home:/test-directory'))
return expect(request('unlink', 'home:/test-directory', createOptions()))
.resolves
.toBe(true);
});

test('#realpath', () => {
const realPath = path.join(core.configuration.tempPath, 'jest/test');

return expect(request('realpath', 'home:/test'))
return expect(request('realpath', 'home:/test', createOptions()))
.resolves
.toBe(realPath);
});
Expand Down
4 changes: 2 additions & 2 deletions __tests__/filesystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ describe('Filesystem', () => {
.toBe('test/jest');
});

test('#mount', () => {
mountpoint = filesystem.mount({
test('#mount', async () => {
mountpoint = await filesystem.mount({
name: 'jest',
attributes: {
root: '/tmp'
Expand Down
60 changes: 35 additions & 25 deletions src/adapters/vfs/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,14 @@ const segments = {

username: {
dynamic: true,
fn: (core, req) => req.session.user.username
fn: (core, session) => session.user.username
}
};

/*
* Gets a segment value
*/
const getSegment = (core, req, seg) => segments[seg] ? segments[seg].fn(core, req) : '';
const getSegment = (core, session, seg) => segments[seg] ? segments[seg].fn(core, session) : '';

/*
* Matches a string for segments
Expand All @@ -97,16 +97,16 @@ const matchSegments = str => (str.match(/(\{\w+\})/g) || []);
/*
* Resolves a string with segments
*/
const resolveSegments = (core, req, str) => matchSegments(str)
.reduce((result, current) => result.replace(current, getSegment(core, req, current.replace(/(\{|\})/g, ''))), str);
const resolveSegments = (core, session, str) => matchSegments(str)
.reduce((result, current) => result.replace(current, getSegment(core, session, current.replace(/(\{|\})/g, ''))), str);

/*
* Resolves a given file path based on a request
* Will take out segments from the resulting string
* and replace them with a list of defined variables
*/
const getRealPath = (core, req, mount, file) => {
const root = resolveSegments(core, req, mount.attributes.root);
const getRealPath = (core, session, mount, file) => {
const root = resolveSegments(core, session, mount.attributes.root);
const str = file.substr(mount.root.length - 1);
return path.join(root, str);
};
Expand All @@ -118,7 +118,7 @@ const getRealPath = (core, req, mount, file) => {
*/
module.exports = (core) => {
const wrapper = (method, cb, ...args) => vfs => (file, options = {}) => {
const promise = Promise.resolve(getRealPath(core, vfs.req, vfs.mount, file))
const promise = Promise.resolve(getRealPath(core, options.session, vfs.mount, file))
.then(realPath => fs[method](realPath, ...args));

return typeof cb === 'function'
Expand All @@ -127,19 +127,17 @@ module.exports = (core) => {
};

const crossWrapper = method => (srcVfs, destVfs) => (src, dest, options = {}) => Promise.resolve({
realSource: getRealPath(core, srcVfs.req, srcVfs.mount, src),
realDest: getRealPath(core, destVfs.req, destVfs.mount, dest)
realSource: getRealPath(core, options.session, srcVfs.mount, src),
realDest: getRealPath(core, options.session, destVfs.mount, dest)
})
.then(({realSource, realDest}) => fs[method](realSource, realDest))
.then(() => true);

return {
watch: (mount, callback) => {
const dest = resolveSegments(core, {
session: {
user: {
username: '**'
}
user: {
username: '**'
}
}, mount.attributes.root);

Expand Down Expand Up @@ -168,6 +166,7 @@ module.exports = (core) => {
/**
* Checks if file exists
* @param {String} file The file path from client
* @param {Object} [options={}] Options
* @return {Promise<boolean, Error>}
*/
exists: wrapper('access', promise => {
Expand All @@ -178,10 +177,11 @@ module.exports = (core) => {
/**
* Get file statistics
* @param {String} file The file path from client
* @param {Object} [options={}] Options
* @return {Object}
*/
stat: vfs => file =>
Promise.resolve(getRealPath(core, vfs.req, vfs.mount, file))
stat: vfs => (file, options = {}) =>
Promise.resolve(getRealPath(core, options.session, vfs.mount, file))
.then(realPath => {
return fs.access(realPath, fs.F_OK)
.then(() => createFileIter(core, path.dirname(realPath), realPath));
Expand All @@ -190,10 +190,11 @@ module.exports = (core) => {
/**
* Reads directory
* @param {String} root The file path from client
* @param {Object} [options={}] Options
* @return {Object[]}
*/
readdir: vfs => root =>
Promise.resolve(getRealPath(core, vfs.req, vfs.mount, root))
readdir: vfs => (root, options) =>
Promise.resolve(getRealPath(core, options.session, vfs.mount, root))
.then(realPath => fs.readdir(realPath).then(files => ({realPath, files})))
.then(({realPath, files}) => {
const promises = files.map(f => createFileIter(core, realPath, root.replace(/\/?$/, '/') + f));
Expand All @@ -203,10 +204,11 @@ module.exports = (core) => {
/**
* Reads file stream
* @param {String} file The file path from client
* @param {Object} [options={}] Options
* @return {stream.Readable}
*/
readfile: vfs => (file, options = {}) =>
Promise.resolve(getRealPath(core, vfs.req, vfs.mount, file))
Promise.resolve(getRealPath(core, options.session, vfs.mount, file))
.then(realPath => fs.stat(realPath).then(stat => ({realPath, stat})))
.then(({realPath, stat}) => {
if (!stat.isFile()) {
Expand All @@ -224,9 +226,10 @@ module.exports = (core) => {
/**
* Creates directory
* @param {String} file The file path from client
* @param {Object} [options={}] Options
* @return {boolean}
*/
mkdir: wrapper('mkdir', (promise, options) => {
mkdir: wrapper('mkdir', (promise, options = {}) => {
return promise
.then(() => true)
.catch(e => {
Expand All @@ -242,13 +245,14 @@ module.exports = (core) => {
* Writes file stream
* @param {String} file The file path from client
* @param {stream.Readable} data The stream
* @param {Object} [options={}] Options
* @return {Promise<boolean, Error>}
*/
writefile: vfs => (file, data) => new Promise((resolve, reject) => {
writefile: vfs => (file, data, options = {}) => new Promise((resolve, reject) => {
// FIXME: Currently this actually copies the file because
// formidable will put this in a temporary directory.
// It would probably be better to do a "rename()" on local filesystems
const realPath = getRealPath(core, vfs.req, vfs.mount, file);
const realPath = getRealPath(core, options.session, vfs.mount, file);

const write = () => {
const stream = fs.createWriteStream(realPath);
Expand All @@ -270,6 +274,7 @@ module.exports = (core) => {
* Renames given file or directory
* @param {String} src The source file path from client
* @param {String} dest The destination file path from client
* @param {Object} [options={}] Options
* @return {boolean}
*/
rename: crossWrapper('rename'),
Expand All @@ -278,24 +283,27 @@ module.exports = (core) => {
* Copies given file or directory
* @param {String} src The source file path from client
* @param {String} dest The destination file path from client
* @param {Object} [options={}] Options
* @return {boolean}
*/
copy: crossWrapper('copy'),

/**
* Removes given file or directory
* @param {String} file The file path from client
* @param {Object} [options={}] Options
* @return {boolean}
*/
unlink: wrapper('remove'),

/**
* Searches for files and folders
* @param {String} file The file path from client
* @param {Object} [options={}] Options
* @return {boolean}
*/
search: vfs => (root, pattern) =>
Promise.resolve(getRealPath(core, vfs.req, vfs.mount, root))
search: vfs => (root, pattern, options = {}) =>
Promise.resolve(getRealPath(core, options.session, vfs.mount, root))
.then(realPath => {
return fh.create()
.paths(realPath)
Expand Down Expand Up @@ -323,16 +331,18 @@ module.exports = (core) => {
/**
* Touches a file
* @param {String} file The file path from client
* @param {Object} [options={}] Options
* @return {boolean}
*/
touch: wrapper('ensureFile'),

/**
* Gets the real filesystem path (internal only)
* @param {String} file The file path from client
* @param {Object} [options={}] Options
* @return {string}
*/
realpath: vfs => file =>
Promise.resolve(getRealPath(core, vfs.req, vfs.mount, file))
realpath: vfs => (file, options = {}) =>
Promise.resolve(getRealPath(core, options.session, vfs.mount, file))
};
};
Loading

0 comments on commit de77937

Please sign in to comment.