-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Expand file tree
/
Copy pathProject.js
More file actions
138 lines (115 loc) · 3.34 KB
/
Copy pathProject.js
File metadata and controls
138 lines (115 loc) · 3.34 KB
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
import { isPlainObject, pick } from 'lodash';
import Project from '../models/project';
import createId from '../utils/createId';
import createApplicationErrorClass from '../utils/createApplicationErrorClass';
import createDefaultFiles from './createDefaultFiles';
export const FileValidationError = createApplicationErrorClass(
'FileValidationError'
);
export const ProjectValidationError = createApplicationErrorClass(
'ProjectValidationError'
);
/**
* This converts between a mongoose Project model
* and the public API Project object properties
*
*/
export function toApi(model) {
return {
id: model.id,
name: model.name
};
}
/**
* Transforms a tree of files matching the APIs DirectoryContents
* format into the data structure stored in mongodb
*
* - flattens the tree into an array of file/folders
* - each file/folder gets a generated BSON-ID
* - each folder has a `children` array containing the IDs of it's children
*/
function transformFilesInner(tree = {}, parentNode) {
const files = [];
const errors = [];
Object.entries(tree).forEach(([name, params]) => {
const id = createId();
const isFolder = params.files != null;
if (isFolder) {
const folder = {
_id: id,
name,
fileType: 'folder',
children: [] // Initialise an empty folder
};
files.push(folder);
// The recursion will return a list of child files/folders
// It will also push the child's id into `folder.children`
const subFolder = transformFilesInner(params.files, folder);
files.push(...subFolder.files);
errors.push(...subFolder.errors);
} else {
const file = {
_id: id,
name,
fileType: 'file'
};
if (typeof params.url === 'string') {
file.url = params.url;
} else if (typeof params.content === 'string') {
file.content = params.content;
} else {
errors.push({ name, message: "missing 'url' or 'content'" });
}
files.push(file);
}
// Push this child's ID onto it's parent's list
// of children
if (parentNode != null) {
parentNode.children.push(id);
}
});
return { files, errors };
}
export function transformFiles(tree = {}) {
const withRoot = {
root: {
files: tree
}
};
const { files, errors } = transformFilesInner(withRoot);
if (errors.length > 0) {
const message = `${
errors.length
} files failed validation. See error.files for individual errors.
Errors:
${errors.map((e) => `* ${e.name}: ${e.message}`).join('\n')}
`;
const error = new FileValidationError(message);
error.files = errors;
throw error;
}
return files;
}
export function containsRootHtmlFile(tree) {
return Object.keys(tree).find((name) => /\.html$/.test(name)) != null;
}
/**
* This converts between the public API's Project object
* properties and a mongoose Project model
*
*/
export function toModel(object) {
let files = [];
let tree = object.files;
if (isPlainObject(tree)) {
if (!containsRootHtmlFile(tree)) {
tree = Object.assign(createDefaultFiles(), tree);
}
files = transformFiles(tree);
} else {
throw new FileValidationError("'files' must be an object");
}
const projectValues = pick(object, ['user', 'name', 'slug']);
projectValues.files = files;
return new Project(projectValues);
}