mirror of
https://github.com/HeyPuter/puter.git
synced 2026-02-09 07:19:44 -06:00
fs/mkdir: add docs spec, update tests, update apitest
This commit is contained in:
69
src/backend/doc/modules/filesystem/API_SPEC.md
Normal file
69
src/backend/doc/modules/filesystem/API_SPEC.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Filesystem API
|
||||
|
||||
Filesystem endpoints allow operations on files and directories in the Puter filesystem.
|
||||
|
||||
## POST `/mkdir` (auth required)
|
||||
|
||||
### Description
|
||||
|
||||
Creates a new directory in the filesystem. Currently support 2 formats:
|
||||
|
||||
- Full path: `{"path": "/foo/bar", args ...}` — this API is used by apitest (`./tools/api-tester/apitest.js`) and aligns more closely with the POSIX spec (https://linux.die.net/man/3/mkdir)
|
||||
- Parent + path: `{"parent": "/foo", "path": "bar", args ...}` — this API is used by `puter-js` via `puter.fs.mkdir`
|
||||
|
||||
A future work would be use a unified format for all filesystem operations.
|
||||
|
||||
### Parameters
|
||||
|
||||
- **path** _- required_
|
||||
- **accepts:** `string`
|
||||
- **description:** The path where the directory should be created
|
||||
- **notes:** Cannot be empty, null, or undefined
|
||||
|
||||
- **parent** _- optional_
|
||||
- **accepts:** `string | UUID`
|
||||
- **description:** The parent directory path or UUID
|
||||
- **notes:** If not provided, path is treated as full path
|
||||
|
||||
- **overwrite** _- optional_
|
||||
- **accepts:** `boolean`
|
||||
- **default:** `false`
|
||||
- **description:** Whether to overwrite existing files/directories
|
||||
|
||||
- **dedupe_name** _- optional_
|
||||
- **accepts:** `boolean`
|
||||
- **default:** `false`
|
||||
- **description:** Whether to automatically rename if name exists
|
||||
|
||||
- **create_missing_parents** _- optional_
|
||||
- **accepts:** `boolean`
|
||||
- **default:** `false`
|
||||
- **description:** Whether to create parent directories if they don't exist
|
||||
- **aliases:** `create_missing_ancestors`
|
||||
|
||||
- **shortcut_to** _- optional_
|
||||
- **accepts:** `string | UUID`
|
||||
- **description:** Creates a shortcut/symlink to the specified target
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "/user/Desktop/new-directory"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"parent": "/user",
|
||||
"path": "Desktop/new-directory"
|
||||
}
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
Returns the created directory's metadata including name, path, uid, and any parent directories created.
|
||||
|
||||
## Other Filesystem Endpoints
|
||||
|
||||
[Additional endpoints would be documented here...]
|
||||
@@ -96,6 +96,12 @@ module.exports = class TestSDK {
|
||||
async case (id, fn) {
|
||||
this.nameStack.push(id);
|
||||
|
||||
// Always reset cwd for top-level cases to prevent them from affecting
|
||||
// each other.
|
||||
if (this.nameStack.length === 1) {
|
||||
this.resetCwd();
|
||||
}
|
||||
|
||||
const tabs = Array(this.nameStack.length - 2).fill(' ').join('');
|
||||
const strid = tabs + this.nameStack.join(` \x1B[36;1m->\x1B[0m `);
|
||||
process.stdout.write(strid + ' ... \n');
|
||||
@@ -167,6 +173,12 @@ module.exports = class TestSDK {
|
||||
cd (path) {
|
||||
this.cwd = path_.posix.join(this.cwd, path);
|
||||
}
|
||||
|
||||
resetCwd () {
|
||||
// TODO (xiaochen): update the hardcoded path to a global constant
|
||||
this.cwd = '/admin/api_test';
|
||||
}
|
||||
|
||||
resolve (path) {
|
||||
if ( path.startsWith('$') ) return path;
|
||||
if ( path.startsWith('/') ) return path;
|
||||
|
||||
@@ -88,48 +88,50 @@ module.exports = {
|
||||
});
|
||||
});
|
||||
|
||||
await t.case('create_missing_parents works (full path api)', async () => {
|
||||
const path = 'a/b/c';
|
||||
await t.case('full path api', async () => {
|
||||
t.cd('full_path_api');
|
||||
|
||||
await t.case('parent directory does not exist', async () => {
|
||||
try {
|
||||
await t.stat('a');
|
||||
} catch (e) {
|
||||
expect(e.response.status).equal(404);
|
||||
}
|
||||
});
|
||||
await t.case('create_missing_parents works', async () => {
|
||||
t.cd('create_missing_parents_works');
|
||||
|
||||
await t.case('mkdir failed without create_missing_parents', async () => {
|
||||
try {
|
||||
await t.mkdir('a/b/c');
|
||||
} catch (e) {
|
||||
expect(e.response.status).equal(409);
|
||||
}
|
||||
});
|
||||
|
||||
await t.case('mkdir succeeds with create_missing_parents', async () => {
|
||||
const result = await t.mkdir('a/b/c', {
|
||||
create_missing_parents: true,
|
||||
await t.case('parent directory does not exist', async () => {
|
||||
try {
|
||||
await t.stat('a');
|
||||
} catch (e) {
|
||||
expect(e.response.status).equal(404);
|
||||
}
|
||||
});
|
||||
expect(result.name).equal('c');
|
||||
});
|
||||
|
||||
await t.case('can stat the directory', async () => {
|
||||
const stat = await t.stat(path);
|
||||
expect(stat.name).equal('c');
|
||||
});
|
||||
await t.case('mkdir succeeds with create_missing_parents', async () => {
|
||||
const result = await t.mkdir('a/b/c', {
|
||||
create_missing_parents: true,
|
||||
});
|
||||
expect(result.name).equal('c');
|
||||
});
|
||||
|
||||
await t.case('can stat the parent directory', async () => {
|
||||
let stat = await t.stat('a');
|
||||
expect(stat.name).equal('a');
|
||||
await t.case('mkdir failed without create_missing_parents', async () => {
|
||||
try {
|
||||
await t.mkdir('a/b/c');
|
||||
} catch (e) {
|
||||
expect(e.response.status).equal(409);
|
||||
}
|
||||
});
|
||||
|
||||
stat = await t.stat('a/b');
|
||||
expect(stat.name).equal('b');
|
||||
await t.case('can stat all directories along the path', async () => {
|
||||
let stat = await t.stat('a');
|
||||
expect(stat.name).equal('a');
|
||||
|
||||
stat = await t.stat('a/b');
|
||||
expect(stat.name).equal('b');
|
||||
|
||||
stat = await t.stat('a/b/c');
|
||||
expect(stat.name).equal('c');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
await t.case('create_missing_parents works (parent + path api)', async () => {
|
||||
const path = 'a/b/c';
|
||||
await t.case('parent + path api', async () => {
|
||||
t.cd('parent_path_api');
|
||||
|
||||
await t.case('parent directory does not exist', async () => {
|
||||
try {
|
||||
@@ -143,7 +145,9 @@ module.exports = {
|
||||
try {
|
||||
await t.mkdir_v2('a/b', 'c');
|
||||
} catch (e) {
|
||||
expect(e.response.status).equal(409);
|
||||
// TODO (xiaochen): `t.mkdir('a/b/c')` throws 409, unify the
|
||||
// behavior of these two cases.
|
||||
expect(e.response.status).equal(422);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user