import { tmpdir } from 'os' import * as path from 'path' import * as Promise from 'bluebird' import * as winston from 'winston' import { CoError } from '../../../modules/co-error' import PresentationSettings from '../../../modules/presentation-settings' import { PresentationClient } from '../../../services/presentation' import clonePresentation from '../src/clonePresentation' import { copy, remove } from '../src/fs' import PresentationFileDiff from '../src/PresentationFileDiff' import PresentationChecksums from '../src/PresentationChecksums' export function pull (presentationChecksums: PresentationChecksums, presentationClient: PresentationClient, presentationDir: string, force: boolean = false): Promise { const update = updatePresentation.bind(null, presentationChecksums, presentationClient, presentationDir); if (force) { return update() } return checkFilesDiff(presentationChecksums, presentationDir) .then(update) } function updatePresentation(presentationChecksums: PresentationChecksums, presentationClient: PresentationClient, presentationDir: string) { const appDir = path.join(presentationDir, 'app'); const backupDir = getTempBackupDir(); return createBackup(appDir, backupDir) .then(() => pullPresentation(presentationClient, presentationDir, appDir, backupDir)) .then(createChecksums(presentationChecksums, presentationDir)) .finally(removeBackup(backupDir)) } function checkFilesDiff(presentationChecksums: PresentationChecksums, presentationDir: string): Promise { return presentationChecksums.findDiff(presentationDir) .then((diff: PresentationFileDiff) => { const { added, updated, removed } = diff; const diffs: string[] = [...added, ...updated, ...removed]; if (diffs.length) { return Promise.reject(new CoError('PRESENTATION_HAS_LOCAL_CHANGES')) } return Promise.resolve() }); } function pullPresentation(presentationClient: PresentationClient, presentationDir: string, appDir: string, backupDir: string): Promise { return getPresentationId(presentationDir) .then(removeLocalPresentation(appDir)) .then(downloadPresentation(presentationClient, presentationDir)) .catch(rollBackChanges(backupDir, appDir)) } function createBackup(srcDir: string, backupDir: string): Promise { return copy(srcDir, backupDir); } function getTempBackupDir() { return path.join(tmpdir(), 'backup' + Date.now()) } function getPresentationId(presentationDir: string): Promise { const presentationSettings = new PresentationSettings(presentationDir) return presentationSettings.get('id') } function downloadPresentation(presentationClient: PresentationClient, presentationDir: string): (id: string) => Promise { return function (presentationId) { return clonePresentation(presentationClient, presentationDir, presentationId) } } function createChecksums(presentationChecksums: PresentationChecksums, presentationDir: string): () => Promise { return function () { return presentationChecksums.createChecksums(presentationDir); } } function removeLocalPresentation(dir: string): (id: string) => Promise { return function (presentationId) { return remove(dir) .then(() => presentationId) } } function rollBackChanges(backupDir: string, srcDir: string): (err: Error) => Promise { return function (err) { return rollBack(backupDir, srcDir) .finally(() => { throw err }) } } function rollBack (backupDir: string, destDir: string) { return Promise.resolve(remove(destDir)) .finally(() => copy(backupDir, destDir)) } function removeBackup(backupDir: string): () => Promise { return function () { return remove(backupDir) } }