@lobb-js/lobb-ext-storage 0.5.8 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/create_tag_on_version_change.yaml +45 -0
- package/.github/workflows/publish_npm_package.yaml +28 -0
- package/.vscode/settings.json +5 -0
- package/CHANGELOG.md +150 -29
- package/README.md +1 -35
- package/extensions/storage/adapters/index.ts +18 -0
- package/extensions/storage/adapters/localAdapter.ts +60 -0
- package/extensions/storage/adapters/storageAdapter.ts +13 -0
- package/extensions/storage/collections/fileSystem.ts +58 -0
- package/extensions/storage/collections/index.ts +12 -0
- package/extensions/storage/config/extensionConfigSchema.ts +15 -0
- package/extensions/storage/index.ts +26 -0
- package/extensions/storage/init.ts +6 -0
- package/extensions/storage/meta.ts +5 -0
- package/extensions/storage/migrations.ts +3 -0
- package/extensions/storage/openapi.ts +84 -0
- package/extensions/storage/studio/tests/fileManager.spec.ts +23 -0
- package/extensions/storage/studio/tests/package.json +1 -0
- package/extensions/storage/studio/tests/playwright.config.cjs +27 -0
- package/extensions/storage/studioExtension.json +1 -0
- package/extensions/storage/tests/configs/simple.ts +95 -0
- package/extensions/storage/tests/directories.test.ts +156 -0
- package/extensions/storage/tests/extraFormData.test.ts +47 -0
- package/extensions/storage/tests/files/rose.jpeg +0 -0
- package/extensions/storage/tests/files.test.ts +292 -0
- package/extensions/storage/tests/forceUpload.test.ts +72 -0
- package/extensions/storage/tests/massRemove.test.ts +189 -0
- package/extensions/storage/tests/meta.test.ts +26 -0
- package/extensions/storage/tests/recursiveDeleteMany.test.ts +208 -0
- package/extensions/storage/tests/recursiveDeleteOne.test.ts +206 -0
- package/extensions/storage/tests/storage/127 +0 -0
- package/extensions/storage/types.ts +11 -0
- package/extensions/storage/utils.ts +87 -0
- package/extensions/storage/workflows/controllers.ts +103 -0
- package/extensions/storage/workflows/index.ts +10 -0
- package/extensions/storage/workflows/services.ts +182 -0
- package/lobb.ts +54 -0
- package/package.json +32 -9
- package/scripts/postpublish.sh +12 -0
- package/scripts/prepublish.sh +17 -0
- package/studio/app.html +12 -0
- package/studio/routes/+layout.svelte +7 -0
- package/studio/routes/+layout.ts +1 -0
- package/studio/routes/[...path]/+page.svelte +6 -0
- package/svelte.config.js +23 -7
- package/todo.md +36 -0
- package/tsconfig.app.json +3 -3
- package/tsconfig.json +11 -5
- package/vite.config.ts +4 -10
- package/components.json +0 -16
- package/index.html +0 -13
- package/src/app.css +0 -124
- package/src/main.ts +0 -14
- /package/{src → extensions/storage/studio}/extension.types.ts +0 -0
- /package/{src → extensions/storage/studio}/index.ts +0 -0
- /package/{src → extensions/storage/studio}/lib/components/childrenFileExplorer.svelte +0 -0
- /package/{src → extensions/storage/studio}/lib/components/explorerNotSupported.svelte +0 -0
- /package/{src → extensions/storage/studio}/lib/components/fileExplorer.svelte +0 -0
- /package/{src → extensions/storage/studio}/lib/components/fileIcon.svelte +0 -0
- /package/{src → extensions/storage/studio}/lib/components/fileManagerBreadCrumbs.svelte +0 -0
- /package/{src → extensions/storage/studio}/lib/components/foreignKeyComponent.svelte +0 -0
- /package/{src → extensions/storage/studio}/lib/components/pages/fileExplorerPage.svelte +0 -0
- /package/{src → extensions/storage/studio}/lib/index.ts +0 -0
- /package/{src → extensions/storage/studio}/lib/utils.ts +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Lobb } from "@lobb-js/core";
|
|
2
|
+
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { createSimpleConfig } from "./configs/simple.ts";
|
|
5
|
+
|
|
6
|
+
describe("Force Upload", () => {
|
|
7
|
+
let lobb: Lobb;
|
|
8
|
+
let baseUrl: string;
|
|
9
|
+
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
const config = createSimpleConfig();
|
|
12
|
+
lobb = await Lobb.init(config);
|
|
13
|
+
baseUrl = `http://127.0.0.1:${lobb.webServer.port}`;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterAll(async () => {
|
|
17
|
+
await lobb.close();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should fail uploading to a non-existent path without force", async () => {
|
|
21
|
+
const image = readFileSync(import.meta.dirname + "/files/rose.jpeg");
|
|
22
|
+
const formData = new FormData();
|
|
23
|
+
formData.append("file", new Blob([image]), "rose.jpeg");
|
|
24
|
+
formData.append("path", "/does_not_exist");
|
|
25
|
+
|
|
26
|
+
const response = await fetch(
|
|
27
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
28
|
+
{ method: "POST", body: formData },
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const data = await response.json();
|
|
32
|
+
expect(data).toMatchObject({ code: "BAD_REQUEST" });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should upload to a non-existent path with force=true, auto-creating directories", async () => {
|
|
36
|
+
const image = readFileSync(import.meta.dirname + "/files/rose.jpeg");
|
|
37
|
+
const formData = new FormData();
|
|
38
|
+
formData.append("file", new Blob([image]), "rose.jpeg");
|
|
39
|
+
formData.append("path", "/auto_created/nested");
|
|
40
|
+
|
|
41
|
+
const response = await fetch(
|
|
42
|
+
`${baseUrl}/api/collections/storage_fs?force=true`,
|
|
43
|
+
{ method: "POST", body: formData },
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const data = await response.json();
|
|
47
|
+
|
|
48
|
+
expect(data).toMatchObject({
|
|
49
|
+
data: {
|
|
50
|
+
name: "rose.jpeg",
|
|
51
|
+
path: "/auto_created/nested",
|
|
52
|
+
type: "file",
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should have auto-created the directory entries", async () => {
|
|
58
|
+
const response = await fetch(
|
|
59
|
+
`${baseUrl}/api/collections/storage_fs?sort=id`,
|
|
60
|
+
{ method: "GET" },
|
|
61
|
+
);
|
|
62
|
+
const data = await response.json();
|
|
63
|
+
|
|
64
|
+
expect(data).toMatchObject({
|
|
65
|
+
data: [
|
|
66
|
+
{ name: "auto_created", path: "/", type: "directory" },
|
|
67
|
+
{ name: "nested", path: "/auto_created/", type: "directory" },
|
|
68
|
+
{ name: "rose.jpeg", path: "/auto_created/nested", type: "file" },
|
|
69
|
+
],
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { Lobb } from "@lobb-js/core";
|
|
2
|
+
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { createSimpleConfig } from "./configs/simple.ts";
|
|
5
|
+
|
|
6
|
+
describe("Mass Remove", () => {
|
|
7
|
+
let lobb: Lobb;
|
|
8
|
+
let baseUrl: string;
|
|
9
|
+
let createdEntriesIds: any[];
|
|
10
|
+
let fileEntryId: string;
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
const config = createSimpleConfig();
|
|
14
|
+
lobb = await Lobb.init(config);
|
|
15
|
+
baseUrl = `http://127.0.0.1:${lobb.webServer.port}`;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterAll(async () => {
|
|
19
|
+
await lobb.close();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
///////////////////////////
|
|
23
|
+
// ADDING DEPENDANT DATA //
|
|
24
|
+
///////////////////////////
|
|
25
|
+
|
|
26
|
+
it("should upload a file successfully", async () => {
|
|
27
|
+
const image = readFileSync(import.meta.dirname + "/files/rose.jpeg");
|
|
28
|
+
const imageFileName = "rose.jpeg";
|
|
29
|
+
|
|
30
|
+
const formData = new FormData();
|
|
31
|
+
formData.append(
|
|
32
|
+
"file",
|
|
33
|
+
new Blob([image]),
|
|
34
|
+
imageFileName,
|
|
35
|
+
);
|
|
36
|
+
formData.append(
|
|
37
|
+
"payload",
|
|
38
|
+
JSON.stringify({
|
|
39
|
+
data: {},
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const response = await fetch(
|
|
44
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
45
|
+
{
|
|
46
|
+
method: "POST",
|
|
47
|
+
body: formData,
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
const data = await response.json();
|
|
51
|
+
|
|
52
|
+
expect(data).toMatchObject({
|
|
53
|
+
data: {
|
|
54
|
+
name: "rose.jpeg",
|
|
55
|
+
path: "/",
|
|
56
|
+
type: "file",
|
|
57
|
+
file_mime_type: "image/jpeg",
|
|
58
|
+
file_size: "1086103",
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should create a directory successfully", async () => {
|
|
64
|
+
const response = await fetch(
|
|
65
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
66
|
+
{
|
|
67
|
+
method: "POST",
|
|
68
|
+
body: JSON.stringify({
|
|
69
|
+
data: {
|
|
70
|
+
name: "test_directory",
|
|
71
|
+
path: "/",
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
const data = await response.json();
|
|
77
|
+
|
|
78
|
+
expect(data).toMatchObject({
|
|
79
|
+
data: {
|
|
80
|
+
name: "test_directory",
|
|
81
|
+
path: "/",
|
|
82
|
+
type: "directory",
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
////////////////////////////
|
|
88
|
+
// Remove MASS OPERATIONS //
|
|
89
|
+
////////////////////////////
|
|
90
|
+
|
|
91
|
+
it("should list all available entries", async () => {
|
|
92
|
+
const response = await fetch(
|
|
93
|
+
`${baseUrl}/api/collections/storage_fs?sort=id`,
|
|
94
|
+
{
|
|
95
|
+
method: "GET",
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
const data = await response.json();
|
|
99
|
+
|
|
100
|
+
expect(data).toMatchObject({
|
|
101
|
+
data: [
|
|
102
|
+
{
|
|
103
|
+
name: "rose.jpeg",
|
|
104
|
+
type: "file",
|
|
105
|
+
path: "/",
|
|
106
|
+
icon: null,
|
|
107
|
+
file_mime_type: "image/jpeg",
|
|
108
|
+
file_size: "1086103",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: "test_directory",
|
|
112
|
+
path: "/",
|
|
113
|
+
type: "directory",
|
|
114
|
+
icon: null,
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
meta: {
|
|
118
|
+
totalCount: 2,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
createdEntriesIds = (data.data as any[]).map((el) => el.id);
|
|
123
|
+
fileEntryId = (data.data as any[]).find((el) => el.type === "file").id;
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("should respond with the content of the file", async () => {
|
|
127
|
+
const response = await fetch(
|
|
128
|
+
`${baseUrl}/api/collections/storage_fs/${fileEntryId}?action=view`,
|
|
129
|
+
{
|
|
130
|
+
method: "GET",
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
await response.text();
|
|
134
|
+
|
|
135
|
+
expect(
|
|
136
|
+
response.headers.get("content-type"),
|
|
137
|
+
).toBe("image/jpeg");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should remove all entries", async () => {
|
|
141
|
+
const response = await fetch(
|
|
142
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
143
|
+
{
|
|
144
|
+
method: "DELETE",
|
|
145
|
+
body: JSON.stringify({
|
|
146
|
+
filter: {
|
|
147
|
+
id: {
|
|
148
|
+
$in: createdEntriesIds,
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
}),
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
const data = await response.json();
|
|
155
|
+
|
|
156
|
+
expect(data).toMatchObject({
|
|
157
|
+
affectedCount: 2,
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("shouldnt be able to get the content of the removed file using deleteMany", async () => {
|
|
162
|
+
const response = await fetch(
|
|
163
|
+
`${baseUrl}/api/collections/storage_fs/${fileEntryId}?action=view`,
|
|
164
|
+
{
|
|
165
|
+
method: "GET",
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
await response.json();
|
|
169
|
+
|
|
170
|
+
expect(response.status).toBe(404);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("should list an empty array since all entries are removed", async () => {
|
|
174
|
+
const response = await fetch(
|
|
175
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
176
|
+
{
|
|
177
|
+
method: "GET",
|
|
178
|
+
},
|
|
179
|
+
);
|
|
180
|
+
const data = await response.json();
|
|
181
|
+
|
|
182
|
+
expect(data).toMatchObject({
|
|
183
|
+
data: [],
|
|
184
|
+
meta: {
|
|
185
|
+
totalCount: 0,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Lobb } from "@lobb-js/core";
|
|
2
|
+
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
|
3
|
+
import { createSimpleConfig } from "./configs/simple.ts";
|
|
4
|
+
|
|
5
|
+
describe("Meta Operations", () => {
|
|
6
|
+
let lobb: Lobb;
|
|
7
|
+
let baseUrl: string;
|
|
8
|
+
|
|
9
|
+
beforeAll(async () => {
|
|
10
|
+
const config = createSimpleConfig();
|
|
11
|
+
lobb = await Lobb.init(config);
|
|
12
|
+
baseUrl = `http://127.0.0.1:${lobb.webServer.port}`;
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterAll(async () => {
|
|
16
|
+
await lobb.close();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should return true for the refresh token", async () => {
|
|
20
|
+
const response = await fetch(`${baseUrl}/api/meta`);
|
|
21
|
+
const data = await response.json();
|
|
22
|
+
expect(data.extensions.storage).toMatchObject({
|
|
23
|
+
storageCollectionName: "storage_fs",
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { Lobb } from "@lobb-js/core";
|
|
2
|
+
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { createSimpleConfig } from "./configs/simple.ts";
|
|
5
|
+
|
|
6
|
+
describe("Recursive Delete Many", () => {
|
|
7
|
+
let lobb: Lobb;
|
|
8
|
+
let baseUrl: string;
|
|
9
|
+
let directoryEntryId: string;
|
|
10
|
+
let fileEntryId: string;
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
const config = createSimpleConfig();
|
|
14
|
+
lobb = await Lobb.init(config);
|
|
15
|
+
baseUrl = `http://127.0.0.1:${lobb.webServer.port}`;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterAll(async () => {
|
|
19
|
+
await lobb.close();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should create a directory successfully", async () => {
|
|
23
|
+
const response = await fetch(
|
|
24
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
25
|
+
{
|
|
26
|
+
method: "POST",
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
data: {
|
|
29
|
+
name: "test",
|
|
30
|
+
path: "/",
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
const data = await response.json();
|
|
36
|
+
|
|
37
|
+
expect(data).toMatchObject({
|
|
38
|
+
data: {
|
|
39
|
+
name: "test",
|
|
40
|
+
path: "/",
|
|
41
|
+
type: "directory",
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
directoryEntryId = data.data.id;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should create a directory inside that directory successfully", async () => {
|
|
49
|
+
const response = await fetch(
|
|
50
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
51
|
+
{
|
|
52
|
+
method: "POST",
|
|
53
|
+
body: JSON.stringify({
|
|
54
|
+
data: {
|
|
55
|
+
name: "nested_test",
|
|
56
|
+
path: "/test",
|
|
57
|
+
},
|
|
58
|
+
}),
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
const data = await response.json();
|
|
62
|
+
|
|
63
|
+
expect(data).toMatchObject({
|
|
64
|
+
data: {
|
|
65
|
+
name: "nested_test",
|
|
66
|
+
path: "/test",
|
|
67
|
+
type: "directory",
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should upload a file inside the test directory successfully", async () => {
|
|
73
|
+
const image = readFileSync(import.meta.dirname + "/files/rose.jpeg");
|
|
74
|
+
const imageFileName = "rose.jpeg";
|
|
75
|
+
|
|
76
|
+
const formData = new FormData();
|
|
77
|
+
formData.append(
|
|
78
|
+
"file",
|
|
79
|
+
new Blob([image]),
|
|
80
|
+
imageFileName,
|
|
81
|
+
);
|
|
82
|
+
formData.append("path", "/test/nested_test");
|
|
83
|
+
|
|
84
|
+
const response = await fetch(
|
|
85
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
86
|
+
{
|
|
87
|
+
method: "POST",
|
|
88
|
+
body: formData,
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
const data = await response.json();
|
|
92
|
+
|
|
93
|
+
expect(data).toMatchObject({
|
|
94
|
+
data: {
|
|
95
|
+
name: "rose.jpeg",
|
|
96
|
+
path: "/test/nested_test",
|
|
97
|
+
type: "file",
|
|
98
|
+
file_mime_type: "image/jpeg",
|
|
99
|
+
file_size: "1086103",
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
fileEntryId = data.data.id;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("should list all available entries", async () => {
|
|
107
|
+
const response = await fetch(
|
|
108
|
+
`${baseUrl}/api/collections/storage_fs?sort=id`,
|
|
109
|
+
{
|
|
110
|
+
method: "GET",
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
const data = await response.json();
|
|
114
|
+
|
|
115
|
+
expect(data).toMatchObject({
|
|
116
|
+
data: [
|
|
117
|
+
{
|
|
118
|
+
name: "test",
|
|
119
|
+
path: "/",
|
|
120
|
+
type: "directory",
|
|
121
|
+
icon: null,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "nested_test",
|
|
125
|
+
path: "/test",
|
|
126
|
+
type: "directory",
|
|
127
|
+
icon: null,
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
path: "/test/nested_test",
|
|
131
|
+
name: "rose.jpeg",
|
|
132
|
+
type: "file",
|
|
133
|
+
file_mime_type: "image/jpeg",
|
|
134
|
+
file_size: "1086103",
|
|
135
|
+
icon: null,
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
meta: {
|
|
139
|
+
totalCount: 3,
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("removing the test directory should remove everything inside it too", async () => {
|
|
145
|
+
const response = await fetch(
|
|
146
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
147
|
+
{
|
|
148
|
+
method: "DELETE",
|
|
149
|
+
body: JSON.stringify({
|
|
150
|
+
filter: {
|
|
151
|
+
id: {
|
|
152
|
+
$in: [directoryEntryId],
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
}),
|
|
156
|
+
},
|
|
157
|
+
);
|
|
158
|
+
const data = await response.json();
|
|
159
|
+
|
|
160
|
+
expect(data).toEqual({
|
|
161
|
+
affectedCount: 1,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const response1 = await fetch(
|
|
165
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
166
|
+
{
|
|
167
|
+
method: "GET",
|
|
168
|
+
},
|
|
169
|
+
);
|
|
170
|
+
const data1 = await response1.json();
|
|
171
|
+
|
|
172
|
+
expect(data1).toMatchObject({
|
|
173
|
+
data: [],
|
|
174
|
+
meta: {
|
|
175
|
+
totalCount: 0,
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("shouldnt be able to get the content of the removed file using deleteMany", async () => {
|
|
181
|
+
const response = await fetch(
|
|
182
|
+
`${baseUrl}/api/collections/storage_fs/${fileEntryId}?action=view`,
|
|
183
|
+
{
|
|
184
|
+
method: "GET",
|
|
185
|
+
},
|
|
186
|
+
);
|
|
187
|
+
await response.json();
|
|
188
|
+
|
|
189
|
+
expect(response.status).toBe(404);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("should list an empty array since all entries are removed", async () => {
|
|
193
|
+
const response = await fetch(
|
|
194
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
195
|
+
{
|
|
196
|
+
method: "GET",
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
const data = await response.json();
|
|
200
|
+
|
|
201
|
+
expect(data).toMatchObject({
|
|
202
|
+
data: [],
|
|
203
|
+
meta: {
|
|
204
|
+
totalCount: 0,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { Lobb } from "@lobb-js/core";
|
|
2
|
+
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { createSimpleConfig } from "./configs/simple.ts";
|
|
5
|
+
|
|
6
|
+
describe("Recursive Delete One", () => {
|
|
7
|
+
let lobb: Lobb;
|
|
8
|
+
let baseUrl: string;
|
|
9
|
+
let directoryEntryId: string;
|
|
10
|
+
let fileEntryId: string;
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
const config = createSimpleConfig();
|
|
14
|
+
lobb = await Lobb.init(config);
|
|
15
|
+
baseUrl = `http://127.0.0.1:${lobb.webServer.port}`;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterAll(async () => {
|
|
19
|
+
await lobb.close();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("Should create a directory successfully", async () => {
|
|
23
|
+
const response = await fetch(
|
|
24
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
25
|
+
{
|
|
26
|
+
method: "POST",
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
data: {
|
|
29
|
+
name: "test",
|
|
30
|
+
path: "/",
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
const data = await response.json();
|
|
36
|
+
|
|
37
|
+
expect(data).toMatchObject({
|
|
38
|
+
data: {
|
|
39
|
+
name: "test",
|
|
40
|
+
path: "/",
|
|
41
|
+
type: "directory",
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
directoryEntryId = data.data.id;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should create a directory inside that directory successfully", async () => {
|
|
49
|
+
const response = await fetch(
|
|
50
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
51
|
+
{
|
|
52
|
+
method: "POST",
|
|
53
|
+
body: JSON.stringify({
|
|
54
|
+
data: {
|
|
55
|
+
name: "nested_test",
|
|
56
|
+
path: "/test",
|
|
57
|
+
},
|
|
58
|
+
}),
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
const data = await response.json();
|
|
62
|
+
|
|
63
|
+
expect(data).toMatchObject({
|
|
64
|
+
data: {
|
|
65
|
+
name: "nested_test",
|
|
66
|
+
path: "/test",
|
|
67
|
+
type: "directory",
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should upload a file inside the test directory successfully", async () => {
|
|
73
|
+
const image = readFileSync(import.meta.dirname + "/files/rose.jpeg");
|
|
74
|
+
const imageFileName = "rose.jpeg";
|
|
75
|
+
|
|
76
|
+
const formData = new FormData();
|
|
77
|
+
formData.append(
|
|
78
|
+
"file",
|
|
79
|
+
new Blob([image]),
|
|
80
|
+
imageFileName,
|
|
81
|
+
);
|
|
82
|
+
formData.append("path", "/test/nested_test");
|
|
83
|
+
|
|
84
|
+
const response = await fetch(
|
|
85
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
86
|
+
{
|
|
87
|
+
method: "POST",
|
|
88
|
+
body: formData,
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
const data = await response.json();
|
|
92
|
+
|
|
93
|
+
expect(data).toMatchObject({
|
|
94
|
+
data: {
|
|
95
|
+
name: "rose.jpeg",
|
|
96
|
+
path: "/test/nested_test",
|
|
97
|
+
type: "file",
|
|
98
|
+
file_mime_type: "image/jpeg",
|
|
99
|
+
file_size: "1086103",
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
fileEntryId = data.data.id;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("should list all available entries", async () => {
|
|
107
|
+
const response = await fetch(
|
|
108
|
+
`${baseUrl}/api/collections/storage_fs?sort=id`,
|
|
109
|
+
{
|
|
110
|
+
method: "GET",
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
const data = await response.json();
|
|
114
|
+
|
|
115
|
+
expect(data).toMatchObject({
|
|
116
|
+
data: [
|
|
117
|
+
{
|
|
118
|
+
name: "test",
|
|
119
|
+
path: "/",
|
|
120
|
+
type: "directory",
|
|
121
|
+
icon: null,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "nested_test",
|
|
125
|
+
path: "/test",
|
|
126
|
+
type: "directory",
|
|
127
|
+
icon: null,
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
path: "/test/nested_test",
|
|
131
|
+
name: "rose.jpeg",
|
|
132
|
+
type: "file",
|
|
133
|
+
icon: null,
|
|
134
|
+
file_mime_type: "image/jpeg",
|
|
135
|
+
file_size: "1086103",
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
meta: {
|
|
139
|
+
totalCount: 3,
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("removing the test directory should remove everything inside it too", async () => {
|
|
145
|
+
const response = await fetch(
|
|
146
|
+
`${baseUrl}/api/collections/storage_fs/${directoryEntryId}`,
|
|
147
|
+
{
|
|
148
|
+
method: "DELETE",
|
|
149
|
+
},
|
|
150
|
+
);
|
|
151
|
+
const data = await response.json();
|
|
152
|
+
|
|
153
|
+
expect(data).toMatchObject({
|
|
154
|
+
data: {
|
|
155
|
+
path: "/",
|
|
156
|
+
name: "test",
|
|
157
|
+
type: "directory",
|
|
158
|
+
icon: null,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const response1 = await fetch(
|
|
163
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
164
|
+
{
|
|
165
|
+
method: "GET",
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
const data1 = await response1.json();
|
|
169
|
+
|
|
170
|
+
expect(data1).toMatchObject({
|
|
171
|
+
data: [],
|
|
172
|
+
meta: {
|
|
173
|
+
totalCount: 0,
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("shouldnt be able to get the content of the removed file using deleteMany", async () => {
|
|
179
|
+
const response = await fetch(
|
|
180
|
+
`${baseUrl}/api/collections/storage_fs/${fileEntryId}?action=view`,
|
|
181
|
+
{
|
|
182
|
+
method: "GET",
|
|
183
|
+
},
|
|
184
|
+
);
|
|
185
|
+
await response.json();
|
|
186
|
+
|
|
187
|
+
expect(response.status).toBe(404);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("should list an empty array since all entries are removed", async () => {
|
|
191
|
+
const response = await fetch(
|
|
192
|
+
`${baseUrl}/api/collections/storage_fs`,
|
|
193
|
+
{
|
|
194
|
+
method: "GET",
|
|
195
|
+
},
|
|
196
|
+
);
|
|
197
|
+
const data = await response.json();
|
|
198
|
+
|
|
199
|
+
expect(data).toMatchObject({
|
|
200
|
+
data: [],
|
|
201
|
+
meta: {
|
|
202
|
+
totalCount: 0,
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
});
|
|
Binary file
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ExposedServiceOutput, ServiceOp } from "@lobb-js/core";
|
|
2
|
+
|
|
3
|
+
export type StorageFsCreateOne = ServiceOp<
|
|
4
|
+
{ file: File; data?: { path?: string }; force?: boolean },
|
|
5
|
+
ExposedServiceOutput
|
|
6
|
+
>;
|
|
7
|
+
|
|
8
|
+
export type StorageFsFindOne = ServiceOp<
|
|
9
|
+
{ id: string; readFile: boolean },
|
|
10
|
+
{ data: { file_mime_type: string; name: string } | null; file: Uint8Array }
|
|
11
|
+
>;
|