diff --git a/src/models/localSyncSettings.model.ts b/src/models/localSyncSettings.model.ts new file mode 100644 index 00000000..baf48346 --- /dev/null +++ b/src/models/localSyncSettings.model.ts @@ -0,0 +1,3 @@ +export class LocalSyncSettings { + public folderPath: string = ""; +} diff --git a/src/service/localSync.service.ts b/src/service/localSync.service.ts new file mode 100644 index 00000000..2f471fdb --- /dev/null +++ b/src/service/localSync.service.ts @@ -0,0 +1,89 @@ +"use strict"; + +import * as fs from "fs-extra"; +import * as path from "path"; +import { LocalSyncSettings } from "../models/localSyncSettings.model"; +import { File, FileService } from "./file.service"; + +export class LocalSyncService { + public static LOCAL_SETTINGS_FILE = "syncLocalSettings.json"; + + public static async ExportFiles( + folderPath: string, + files: File[] + ): Promise { + if (!folderPath || !files) { + return false; + } + + await fs.mkdirs(folderPath); + + for (const file of files) { + const fileName = file.gistName || file.fileName; + const targetPath = await FileService.CreateDirTree( + LocalSyncService.EnsureTrailingSeparator(folderPath), + fileName + ); + const written = await FileService.WriteFile(targetPath, file.content); + + if (!written) { + return false; + } + } + + return true; + } + + public static async ImportFiles( + folderPath: string, + customSettings: any = LocalSyncService.CreateDefaultImportConfig() + ): Promise { + if (!folderPath || !(await FileService.IsDirectory(folderPath))) { + return []; + } + + return FileService.ListFiles(folderPath, customSettings); + } + + public static async ReadSettings( + filePath: string + ): Promise { + if (!(await FileService.FileExists(filePath))) { + return new LocalSyncSettings(); + } + + const content = await FileService.ReadFile(filePath); + return Object.assign(new LocalSyncSettings(), JSON.parse(content)); + } + + public static async WriteSettings( + filePath: string, + settings: LocalSyncSettings + ): Promise { + await fs.mkdirs(path.dirname(filePath)); + return FileService.WriteFile(filePath, JSON.stringify(settings, null, 2)); + } + + private static EnsureTrailingSeparator(folderPath: string): string { + return folderPath.endsWith(path.sep) ? folderPath : folderPath + path.sep; + } + + private static CreateDefaultImportConfig() { + return { + ignoreUploadFiles: [ + "state.*", + LocalSyncService.LOCAL_SETTINGS_FILE, + ".DS_Store", + "sync.lock", + "projects.json", + "projects_cache_vscode.json", + "projects_cache_git.json", + "projects_cache_svn.json", + "gpm_projects.json", + "gpm-recentItems.json" + ], + ignoreUploadFolders: ["workspaceStorage"], + supportedFileExtensions: ["json", "code-snippets"] + }; + } +} diff --git a/test/service/localSyncService/localSyncService.test.ts b/test/service/localSyncService/localSyncService.test.ts new file mode 100644 index 00000000..fc699f1a --- /dev/null +++ b/test/service/localSyncService/localSyncService.test.ts @@ -0,0 +1,79 @@ +import { expect } from "chai"; +import * as fs from "fs-extra"; +import * as os from "os"; +import * as path from "path"; + +import { LocalSyncSettings } from "../../../src/models/localSyncSettings.model"; +import { File } from "../../../src/service/file.service"; +import { LocalSyncService } from "../../../src/service/localSync.service"; + +describe("LocalSyncService", () => { + let syncFolder: string; + + beforeEach(async () => { + syncFolder = await fs.mkdtemp(path.join(os.tmpdir(), "settings-sync-")); + }); + + afterEach(async () => { + await fs.remove(syncFolder); + }); + + it("should export files to the selected folder", async () => { + const files = [ + new File("settings.json", '{"editor.tabSize": 2}', "", "settings.json"), + new File( + "snippets.code-snippets", + '{"hello": {"body": "world"}}', + "", + "snippets|snippets.code-snippets" + ) + ]; + + const result = await LocalSyncService.ExportFiles(syncFolder, files); + + expect(result).to.be.equals(true); + expect( + await fs.readFile(path.join(syncFolder, "settings.json"), "utf8") + ).to.be.equals('{"editor.tabSize": 2}'); + expect( + await fs.readFile( + path.join(syncFolder, "snippets", "snippets.code-snippets"), + "utf8" + ) + ).to.be.equals('{"hello": {"body": "world"}}'); + }); + + it("should import files from the selected folder", async () => { + await fs.writeFile( + path.join(syncFolder, "settings.json"), + '{"editor.fontSize": 14}' + ); + + const files = await LocalSyncService.ImportFiles(syncFolder); + + expect(files.map(file => file.fileName)).to.contain("settings.json"); + expect(files.map(file => file.content)).to.contain( + '{"editor.fontSize": 14}' + ); + }); + + it("should read and write local sync settings", async () => { + const settingsPath = path.join(syncFolder, "syncLocalSettings.json"); + const settings = new LocalSyncSettings(); + settings.folderPath = syncFolder; + + const written = await LocalSyncService.WriteSettings(settingsPath, settings); + const actual = await LocalSyncService.ReadSettings(settingsPath); + + expect(written).to.be.equals(true); + expect(actual.folderPath).to.be.equals(syncFolder); + }); + + it("should return default settings if local settings file does not exist", async () => { + const actual = await LocalSyncService.ReadSettings( + path.join(syncFolder, "syncLocalSettings.json") + ); + + expect(actual.folderPath).to.be.equals(""); + }); +});