-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmimeHandlers.ts
154 lines (140 loc) · 6.2 KB
/
mimeHandlers.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import { Message } from "rs-core/Message.ts";
import { Url } from "rs-core/Url.ts";
import { DirDescriptor } from "rs-core/DirDescriptor.ts";
import { last, slashTrimLeft } from "rs-core/utility/utility.ts";
import { AsyncQueue } from "rs-core/utility/asyncQueue.ts";
import { zip } from "./pipeline/zipJoiner.ts"
type MimeHandler = (msg: Message, url: Url, requestInternal: (req: Message) => Promise<Message>) => Promise<Message>;
const generatePaths = async function* (msg: Message, requestInternal?: (req: Message) => Promise<Message>): AsyncGenerator<[ Message, DirDescriptor ]> {
let dir = ((await msg.data!.asJson()) || []) as DirDescriptor | DirDescriptor[];
if (Array.isArray(dir)) dir = dir[0];
yield [ msg, { ...dir } ];
if (requestInternal) {
const subdirs = dir.paths.filter(([ p ]) => p.endsWith('/'));
for (const [ subdir ] of subdirs) {
const newUrl = msg.url.follow(subdir);
newUrl.query['$list'] = [ "recursive,details,all" ];
const msgOut = await requestInternal(msg.copy().setUrl(newUrl).setData(null, ''));
const dirList = ((await msgOut.data!.asJson()) || []) as DirDescriptor[];
for (const resDir of dirList) {
yield [ msgOut, { ...resDir } ];
}
}
}
}
const dirToItems = async (msg: Message, dir: DirDescriptor, requestInternal: (req: Message) => Promise<Message>) => {
const fetchAllMessages = dir.paths
.filter(([ path ]) => !path.endsWith('/'))
.map(([ path ]) => {
const url = msg.url.copy();
url.servicePath = dir.path + path;
return msg.copy().setUrl(url);
});
const fullList: Record<string, unknown> = {};
await Promise.all(fetchAllMessages
.map(msg => requestInternal(msg)
.then(msg => msg.data!.asJson().then(data => fullList[slashTrimLeft(dir.path) + msg.url.resourceName] = data))
));
return fullList;
}
const dirToQueue = (basePath: string, msg: Message, dir: DirDescriptor, requestInternal: (req: Message) => Promise<Message>) => {
if (basePath === '/') basePath = '';
const fetchAllMessages = dir.paths
.filter(([ path ]) => !path.endsWith('/'))
.map(([ path ]) => {
const url = msg.url.copy();
url.servicePath = dir.path + path;
const name = url.servicePath.substring(basePath.length);
return msg.copy().setUrl(url).setName(name);
});
const dirFetchQueue = new AsyncQueue<Message>(fetchAllMessages.length)
fetchAllMessages
.forEach(msg => dirFetchQueue.enqueue(requestInternal(msg)));
return dirFetchQueue;
}
const final = (s: string) => {
const words = s.split('/');
return last(words) === '' ? words.slice(-2)[0] + '/' : last(words);
};
const extractFragment: MimeHandler = async (msg, url) => {
if (url.fragment && msg.data) {
await msg.data.extractPathIfJson(url.fragment);
}
return msg;
}
export const mimeHandlers: { [ mimeType: string ]: MimeHandler } = {
"application/json": extractFragment,
"application/schema+json": extractFragment,
"inode/directory+json": async (msg, url, requestInternal) => {
const listFlags = (url.query['$list'] || []).join(',') as string;
let isRecursive = listFlags.includes('recursive');
const getItems = listFlags.includes('items');
const details = listFlags.includes('details');
let pathsOnly = !(details || getItems);
const allFiles = listFlags.includes('all');
const fileInfo = listFlags.includes('fileinfo') && pathsOnly;
const noDirs = listFlags.includes('nodirs');
const isZip = listFlags.includes('zip');
const zipQueue = new AsyncQueue<Message>();
if (isZip) {
isRecursive = true;
pathsOnly = true;
}
let results = [] as any[];
let basePath = msg.url.servicePath;
if (basePath === '/') basePath = '';
for await (const [ resMsg, resDir ] of generatePaths(msg, isRecursive ? requestInternal : undefined)) {
if (!resDir) continue;
if (!allFiles) {
resDir.paths = resDir.paths.filter(([ p ]) => !final(p).startsWith('.'));
}
if (noDirs) {
resDir.paths = resDir.paths.filter(([ p ]) => !p.endsWith('/'));
}
let result = resDir as any;
// transform resDir to get appropriate list items
if (getItems) {
result = await dirToItems(resMsg, resDir, requestInternal);
results.push(result);
} else if (isZip) {
const dirQueue = dirToQueue(msg.url.servicePath, resMsg, resDir, requestInternal);
zipQueue.enqueue(dirQueue);
dirQueue.close();
} else if (pathsOnly) {
const relPath = (resDir.path === '/' ? '' : resDir.path).substring(basePath.length);
results = results.concat(resDir.paths.map(([p, ...rest]) => [relPath + p, ...rest]));
} else {
results.push(result);
}
}
if (isZip) {
zipQueue.close();
let zipMsg = await zip(zipQueue, msg.tenant);
if (zipMsg === null) zipMsg = msg.setStatus(500, 'Zip output null');
return zipMsg;
}
// transform output list
if (isRecursive) {
if (getItems) {
results = Object.assign(results[0], ...results.slice(1));
} else if (!fileInfo) {
// list of lists
results = results.flatMap(i => i);
}
} else {
if (!pathsOnly) {
results = results[0];
} else if (!fileInfo) {
results = results.map(([ p ]) => p);
}
}
if (getItems) {
// list of objects
let obj = results as unknown as Record<string, unknown>;
const urlRemoveChars = (url.servicePath === '/' ? 0 : url.servicePath.length);
obj = Object.fromEntries(Object.entries(obj).map(([k, v]) => [ k.substr(urlRemoveChars), v ]));
return msg.setDirectoryJson(obj);
}
return msg.setDirectoryJson(results);
}
}