Skip to content

Commit 2a60d38

Browse files
committed
In project.ts:
Added CURRENT_VERSION. Added type ProjectInfo. Added functions saveProjectInfo, and deleteProjectInfo. Call saveProjectInfo after saving or deleting a module. Call deleteProjectInfo when deleting a project. Updated downloadProject and uploadProject to download/upload all files in the project. Updated processUploadedBlob to handle the project.info.json file and module files. In module_content.ts: Updated parseModuleContentText to check module content.
1 parent 05a8222 commit 2a60d38

File tree

3 files changed

+92
-32
lines changed

3 files changed

+92
-32
lines changed

src/editor/editor.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,9 @@ export class Editor {
320320
try {
321321
await this.storage.saveFile(this.modulePath, moduleContentText);
322322
this.moduleContentText = moduleContentText;
323+
if (this.currentProject) {
324+
await storageProject.saveProjectInfo(this.storage, this.currentProject.projectName);
325+
}
323326
} catch (e) {
324327
throw e;
325328
}

src/storage/module_content.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,15 @@ export function makeModuleContentText(
147147
export function parseModuleContentText(moduleContentText: string): ModuleContent {
148148
const parsedContent = JSON.parse(moduleContentText);
149149
fixOldParsedContent(parsedContent);
150+
if (!('moduleType' in parsedContent) ||
151+
!('moduleId' in parsedContent) ||
152+
!('blocks' in parsedContent) ||
153+
!('mechanisms' in parsedContent) ||
154+
!('components' in parsedContent) ||
155+
!('events' in parsedContent) ||
156+
!('methods' in parsedContent)) {
157+
throw new Error('Module content text is not valid.');
158+
}
150159
return new ModuleContent(
151160
parsedContent.moduleType,
152161
parsedContent.moduleId,

src/storage/project.ts

Lines changed: 80 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,17 @@ export type Project = {
3535
opModes: storageModule.OpMode[],
3636
};
3737

38+
const CURRENT_VERSION = '0.0.1';
39+
40+
type ProjectInfo = {
41+
version: string,
42+
};
43+
3844
/**
3945
* Returns the list of project names.
4046
*/
4147
export async function listProjectNames(storage: commonStorage.Storage): Promise<string[]> {
42-
const filePathRegexPattern = '.*/Robot\.robot\.json$';
48+
const filePathRegexPattern = storageNames.REGEX_ROBOT_MODULE_PATH;
4349
const robotModulePaths: string[] = await storage.listFilePaths(filePathRegexPattern);
4450

4551
const projectNames: string[] = [];
@@ -119,6 +125,7 @@ export async function createProject(
119125
const opmodeContent = storageModuleContent.newOpModeContent(
120126
newProjectName, storageNames.CLASS_NAME_TELEOP);
121127
await storage.saveFile(opmodePath, opmodeContent);
128+
await saveProjectInfo(storage, newProjectName);
122129
}
123130

124131
/**
@@ -161,6 +168,10 @@ async function renameOrCopyProject(
161168
await storage.deleteFile(modulePath);
162169
}
163170
}
171+
await saveProjectInfo(storage, newProjectName);
172+
if (rename) {
173+
await deleteProjectInfo(storage, projectName);
174+
}
164175
}
165176

166177
/**
@@ -177,6 +188,7 @@ export async function deleteProject(
177188
for (const modulePath of modulePaths) {
178189
await storage.deleteFile(modulePath);
179190
}
191+
await deleteProjectInfo(storage, projectName);
180192
}
181193

182194
/**
@@ -215,6 +227,7 @@ export async function addModuleToProject(
215227
} as storageModule.OpMode);
216228
break;
217229
}
230+
await saveProjectInfo(storage, project.projectName);
218231
}
219232

220233
/**
@@ -238,6 +251,7 @@ export async function removeModuleFromProject(
238251
break;
239252
}
240253
await storage.deleteFile(modulePath);
254+
await saveProjectInfo(storage, project.projectName);
241255
}
242256
}
243257

@@ -331,6 +345,7 @@ async function renameOrCopyModule(
331345
break;
332346
}
333347
}
348+
await saveProjectInfo(storage, project.projectName);
334349

335350
return newModulePath;
336351
}
@@ -404,20 +419,20 @@ export function findModuleByModulePath(project: Project, modulePath: string): st
404419
*/
405420
export async function downloadProject(
406421
storage: commonStorage.Storage, projectName: string): Promise<string> {
407-
const modulePaths: string[] = await storage.listFilePaths(
408-
storageNames.makeModulePathRegexPattern(projectName));
409-
410-
const fileNameToModuleContentText: {[fileName: string]: string} = {}; // value is module content text
411-
for (const modulePath of modulePaths) {
412-
const fileName = storageNames.getFileName(modulePath);
413-
const moduleContentText = await storage.fetchFileContentText(modulePath);
414-
fileNameToModuleContentText[fileName] = moduleContentText;
422+
const filePaths: string[] = await storage.listFilePaths(
423+
storageNames.makeFilePathRegexPattern(projectName));
424+
425+
const fileNameToFileContentText: {[fileName: string]: string} = {}; // value is file content text
426+
for (const filePath of filePaths) {
427+
const fileName = storageNames.getFileName(filePath);
428+
const fileContentText = await storage.fetchFileContentText(filePath);
429+
fileNameToFileContentText[fileName] = fileContentText;
415430
}
416431

417432
const zip = new JSZip();
418-
for (const fileName in fileNameToModuleContentText) {
419-
const moduleContentText = fileNameToModuleContentText[fileName];
420-
zip.file(fileName, moduleContentText);
433+
for (const fileName in fileNameToFileContentText) {
434+
const fileContentText = fileNameToFileContentText[fileName];
435+
zip.file(fileName, fileContentText);
421436
}
422437
const content = await zip.generateAsync({ type: "blob" });
423438
return URL.createObjectURL(content);
@@ -436,20 +451,19 @@ export function makeUploadProjectName(
436451
export async function uploadProject(
437452
storage: commonStorage.Storage, projectName: string, blobUrl: string): Promise<void> {
438453
// Process the uploaded blob to get the file names and contents.
439-
const fileNameToModuleContentText = await processUploadedBlob(blobUrl);
440-
441-
// Save each module.
442-
for (const fileName in fileNameToModuleContentText) {
443-
const moduleContentText = fileNameToModuleContentText[fileName];
444-
const className = storageNames.getClassName(fileName);
445-
const moduleType = storageNames.getModuleType(fileName);
446-
const modulePath = storageNames.makeModulePath(projectName, className, moduleType);
447-
await storage.saveFile(modulePath, moduleContentText);
454+
const fileNameToFileContentText = await processUploadedBlob(blobUrl);
455+
456+
// Save each file.
457+
for (const fileName in fileNameToFileContentText) {
458+
const fileContentText = fileNameToFileContentText[fileName];
459+
const filePath = storageNames.makeFilePath(projectName, fileName);
460+
await storage.saveFile(filePath, fileContentText);
448461
}
462+
await saveProjectInfo(storage, projectName);
449463
}
450464

451465
/**
452-
* Process the uploaded blob to get the module file names and file contents.
466+
* Process the uploaded blob to get the file names and file contents.
453467
*/
454468
async function processUploadedBlob(blobUrl: string): Promise<{ [fileName: string]: string }> {
455469

@@ -473,22 +487,56 @@ async function processUploadedBlob(blobUrl: string): Promise<{ [fileName: string
473487
})
474488
);
475489

476-
// Process each module's content.
490+
// Process each file's content.
477491
let foundRobot = false;
478-
const fileNameToModuleContentText: { [fileName: string]: string } = {}; // value is module content text
492+
const fileNameToFileContentText: { [fileName: string]: string } = {}; // value is file content text
479493
for (const fileName in files) {
480-
const moduleType = storageNames.getModuleType(fileName);
481-
if (moduleType === storageModule.ModuleType.ROBOT) {
482-
foundRobot = true;
494+
if (storageNames.isValidProjectInfoFileName(fileName)) {
495+
// Make sure we can parse the content.
496+
parseProjectInfoContentText(files[fileName]);
497+
} else if (storageNames.isValidModuleFileName(fileName)) {
498+
const moduleType = storageNames.getModuleType(fileName);
499+
if (moduleType === storageModule.ModuleType.ROBOT) {
500+
foundRobot = true;
501+
}
502+
// Make sure we can parse the content.
503+
storageModuleContent.parseModuleContentText(files[fileName]);
504+
} else {
505+
throw new Error('Uploaded project file contains one or more unexpected files.');
483506
}
484-
// Make sure we can parse the content.
485-
const moduleContent = storageModuleContent.parseModuleContentText(files[fileName]);
486-
fileNameToModuleContentText[fileName] = moduleContent.getModuleContentText();
507+
fileNameToFileContentText[fileName] = files[fileName];
487508
}
488509

489510
if (!foundRobot) {
490-
throw new Error('Uploaded file did not contain a Robot.');
511+
throw new Error('Uploaded project file did not contain a Robot.');
512+
}
513+
514+
return fileNameToFileContentText;
515+
}
516+
517+
export async function saveProjectInfo(
518+
storage: commonStorage.Storage, projectName: string): Promise<void> {
519+
const projectInfo: ProjectInfo = {
520+
version: CURRENT_VERSION,
521+
};
522+
const projectInfoContentText = JSON.stringify(projectInfo, null, 2);
523+
const projectInfoPath = storageNames.makeProjectInfoPath(projectName);
524+
await storage.saveFile(projectInfoPath, projectInfoContentText);
525+
}
526+
527+
function parseProjectInfoContentText(projectInfoContentText: string): ProjectInfo {
528+
const parsedContent = JSON.parse(projectInfoContentText);
529+
if (!('version' in parsedContent)) {
530+
throw new Error('Project info content text is not valid.');
491531
}
532+
const projectInfo: ProjectInfo = {
533+
version: parsedContent.version,
534+
};
535+
return projectInfo;
536+
}
492537

493-
return fileNameToModuleContentText;
538+
async function deleteProjectInfo(
539+
storage: commonStorage.Storage, projectName: string): Promise<void> {
540+
const projectInfoPath = storageNames.makeProjectInfoPath(projectName);
541+
await storage.deleteFile(projectInfoPath);
494542
}

0 commit comments

Comments
 (0)