Skip to content

Commit fee100d

Browse files
XiaochenCuiKernelDeimos
authored andcommitted
fs/mkdir: add docs spec, update tests, update apitest
1 parent 7e20039 commit fee100d

File tree

3 files changed

+119
-34
lines changed

3 files changed

+119
-34
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Filesystem API
2+
3+
Filesystem endpoints allow operations on files and directories in the Puter filesystem.
4+
5+
## POST `/mkdir` (auth required)
6+
7+
### Description
8+
9+
Creates a new directory in the filesystem. Currently support 2 formats:
10+
11+
- 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)
12+
- Parent + path: `{"parent": "/foo", "path": "bar", args ...}` — this API is used by `puter-js` via `puter.fs.mkdir`
13+
14+
A future work would be use a unified format for all filesystem operations.
15+
16+
### Parameters
17+
18+
- **path** _- required_
19+
- **accepts:** `string`
20+
- **description:** The path where the directory should be created
21+
- **notes:** Cannot be empty, null, or undefined
22+
23+
- **parent** _- optional_
24+
- **accepts:** `string | UUID`
25+
- **description:** The parent directory path or UUID
26+
- **notes:** If not provided, path is treated as full path
27+
28+
- **overwrite** _- optional_
29+
- **accepts:** `boolean`
30+
- **default:** `false`
31+
- **description:** Whether to overwrite existing files/directories
32+
33+
- **dedupe_name** _- optional_
34+
- **accepts:** `boolean`
35+
- **default:** `false`
36+
- **description:** Whether to automatically rename if name exists
37+
38+
- **create_missing_parents** _- optional_
39+
- **accepts:** `boolean`
40+
- **default:** `false`
41+
- **description:** Whether to create parent directories if they don't exist
42+
- **aliases:** `create_missing_ancestors`
43+
44+
- **shortcut_to** _- optional_
45+
- **accepts:** `string | UUID`
46+
- **description:** Creates a shortcut/symlink to the specified target
47+
48+
### Example
49+
50+
```json
51+
{
52+
"path": "/user/Desktop/new-directory"
53+
}
54+
```
55+
56+
```json
57+
{
58+
"parent": "/user",
59+
"path": "Desktop/new-directory"
60+
}
61+
```
62+
63+
### Response
64+
65+
Returns the created directory's metadata including name, path, uid, and any parent directories created.
66+
67+
## Other Filesystem Endpoints
68+
69+
[Additional endpoints would be documented here...]

tools/api-tester/lib/TestSDK.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ module.exports = class TestSDK {
9696
async case (id, fn) {
9797
this.nameStack.push(id);
9898

99+
// Always reset cwd for top-level cases to prevent them from affecting
100+
// each other.
101+
if (this.nameStack.length === 1) {
102+
this.resetCwd();
103+
}
104+
99105
const tabs = Array(this.nameStack.length - 2).fill(' ').join('');
100106
const strid = tabs + this.nameStack.join(` \x1B[36;1m->\x1B[0m `);
101107
process.stdout.write(strid + ' ... \n');
@@ -167,6 +173,12 @@ module.exports = class TestSDK {
167173
cd (path) {
168174
this.cwd = path_.posix.join(this.cwd, path);
169175
}
176+
177+
resetCwd () {
178+
// TODO (xiaochen): update the hardcoded path to a global constant
179+
this.cwd = '/admin/api_test';
180+
}
181+
170182
resolve (path) {
171183
if ( path.startsWith('$') ) return path;
172184
if ( path.startsWith('/') ) return path;

tools/api-tester/tests/mkdir.js

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -88,48 +88,50 @@ module.exports = {
8888
});
8989
});
9090

91-
await t.case('create_missing_parents works (full path api)', async () => {
92-
const path = 'a/b/c';
93-
94-
await t.case('parent directory does not exist', async () => {
95-
try {
96-
await t.stat('a');
97-
} catch (e) {
98-
expect(e.response.status).equal(404);
99-
}
100-
});
91+
await t.case('full path api', async () => {
92+
t.cd('full_path_api');
93+
94+
await t.case('create_missing_parents works', async () => {
95+
t.cd('create_missing_parents_works');
96+
97+
await t.case('parent directory does not exist', async () => {
98+
try {
99+
await t.stat('a');
100+
} catch (e) {
101+
expect(e.response.status).equal(404);
102+
}
103+
});
101104

102-
await t.case('mkdir failed without create_missing_parents', async () => {
103-
try {
104-
await t.mkdir('a/b/c');
105-
} catch (e) {
106-
expect(e.response.status).equal(409);
107-
}
108-
});
105+
await t.case('mkdir succeeds with create_missing_parents', async () => {
106+
const result = await t.mkdir('a/b/c', {
107+
create_missing_parents: true,
108+
});
109+
expect(result.name).equal('c');
110+
});
109111

110-
await t.case('mkdir succeeds with create_missing_parents', async () => {
111-
const result = await t.mkdir('a/b/c', {
112-
create_missing_parents: true,
112+
await t.case('mkdir failed without create_missing_parents', async () => {
113+
try {
114+
await t.mkdir('a/b/c');
115+
} catch (e) {
116+
expect(e.response.status).equal(409);
117+
}
113118
});
114-
expect(result.name).equal('c');
115-
});
116119

117-
await t.case('can stat the directory', async () => {
118-
const stat = await t.stat(path);
119-
expect(stat.name).equal('c');
120-
});
120+
await t.case('can stat all directories along the path', async () => {
121+
let stat = await t.stat('a');
122+
expect(stat.name).equal('a');
121123

122-
await t.case('can stat the parent directory', async () => {
123-
let stat = await t.stat('a');
124-
expect(stat.name).equal('a');
124+
stat = await t.stat('a/b');
125+
expect(stat.name).equal('b');
125126

126-
stat = await t.stat('a/b');
127-
expect(stat.name).equal('b');
127+
stat = await t.stat('a/b/c');
128+
expect(stat.name).equal('c');
129+
});
128130
});
129131
});
130132

131-
await t.case('create_missing_parents works (parent + path api)', async () => {
132-
const path = 'a/b/c';
133+
await t.case('parent + path api', async () => {
134+
t.cd('parent_path_api');
133135

134136
await t.case('parent directory does not exist', async () => {
135137
try {
@@ -143,7 +145,9 @@ module.exports = {
143145
try {
144146
await t.mkdir_v2('a/b', 'c');
145147
} catch (e) {
146-
expect(e.response.status).equal(409);
148+
// TODO (xiaochen): `t.mkdir('a/b/c')` throws 409, unify the
149+
// behavior of these two cases.
150+
expect(e.response.status).equal(422);
147151
}
148152
});
149153

0 commit comments

Comments
 (0)