storageService.ts 8.44 KB
Newer Older
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
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

'use strict';

import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { getLogger, Logger } from '../../common/log';
import { uniqueString } from '../../common/utils';
import { tarAdd } from '../common/util';

export abstract class StorageService {
    protected localRoot: string = "";
    protected remoteRoot: string = "";
    protected logger: Logger;

    protected abstract internalConfig(key: string, value: string): void;
    protected abstract async internalRemove(remotePath: string, isDirectory: boolean, isRecursive: boolean): Promise<void>;
    protected abstract async internalRename(remotePath: string, newName: string): Promise<void>;
    protected abstract async internalMkdir(remotePath: string): Promise<void>;
    protected abstract async internalCopy(sourcePath: string, targetPath: string, isDirectory: boolean, isFromRemote: boolean, isToRemote: boolean): Promise<string>;
    protected abstract async internalExists(remotePath: string): Promise<boolean>;
    protected abstract async internalRead(remotePath: string, offset: number, length: number): Promise<string>;
    protected abstract async internalList(remotePath: string): Promise<string[]>;
    protected abstract async internalAttach(remotePath: string, content: string): Promise<boolean>;
    protected abstract internalIsRelativePath(path: string): boolean;
    protected abstract internalJoin(...paths: string[]): string;
    protected abstract internalDirname(...paths: string[]): string;
    protected abstract internalBasename(...paths: string[]): string;

    constructor() {
        this.logger = getLogger();
    }

    public initialize(localRoot: string, remoteRoot: string): void {
        this.logger.debug(`Initializing storage to local: ${localRoot} remote: ${remoteRoot}`);
        this.localRoot = localRoot;
        this.remoteRoot = remoteRoot;
    }

    public async rename(remotePath: string, newName: string): Promise<void> {
        remotePath = this.expandPath(true, remotePath);
        this.logger.debug(`rename remotePath: ${remotePath} to: ${newName}`);
        await this.internalRename(remotePath, newName);
    }

    public async createDirectory(remotePath: string): Promise<void> {
        remotePath = this.expandPath(true, remotePath);
        this.logger.debug(`create remotePath: ${remotePath}`);
        await this.internalMkdir(remotePath);
    }

    public async copyDirectory(localPath: string, remotePath: string, asGzip: boolean = false): Promise<string> {
        localPath = this.expandPath(false, localPath);
        remotePath = this.expandPath(true, remotePath);
        this.logger.debug(`copy localPath: ${localPath} to remotePath: ${remotePath}, asGzip ${asGzip}`);
        if (!await this.internalExists(remotePath)) {
            await this.internalMkdir(remotePath);
        }

        if (asGzip) {
            const localPathBaseName = path.basename(localPath);
            const tempTarFileName = `nni_tmp_${localPathBaseName}_${uniqueString(5)}.tar.gz`;
            const tarFileName = `${localPathBaseName}.tar.gz`;
            const localTarPath: string = path.join(os.tmpdir(), tempTarFileName);
            await tarAdd(localTarPath, localPath);
            await this.internalCopy(localTarPath, remotePath, false, false, true);
            const remoteFileName = this.internalJoin(remotePath, tempTarFileName);
            await this.internalRename(remoteFileName, tarFileName);
            await fs.promises.unlink(localTarPath);

            remotePath = this.internalJoin(remotePath, tarFileName);
        } else {
            await this.internalCopy(localPath, remotePath, true, false, true);
            remotePath = this.internalJoin(remotePath, path.basename(localPath));
        }

        return remotePath;
    }

    public async copyDirectoryBack(remotePath: string, localPath: string): Promise<string> {
        localPath = this.expandPath(false, localPath);
        remotePath = this.expandPath(true, remotePath);
        this.logger.debug(`copy remotePath: ${remotePath} to localPath: ${localPath}`);
86
        return await this.internalCopy(remotePath, localPath, true, true, false);
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
    }

    public async removeDirectory(remotePath: string, isRecursive: boolean): Promise<void> {
        remotePath = this.expandPath(true, remotePath);
        this.logger.debug(`remove remotePath: ${remotePath}`);
        await this.internalRemove(remotePath, true, isRecursive);
    }

    public async readFileContent(remotePath: string, offset: number = -1, length: number = -1): Promise<string> {
        remotePath = this.expandPath(true, remotePath);
        this.logger.debug(`read remote file: ${remotePath}, offset: ${offset}, length: ${length}`);
        return this.internalRead(remotePath, offset, length);
    }

    public async listDirectory(remotePath: string): Promise<string[]> {
        remotePath = this.expandPath(true, remotePath);
        this.logger.debug(`list remotePath: ${remotePath}`);
        return await this.internalList(remotePath);
    }

    public async exists(remotePath: string): Promise<boolean> {
        remotePath = this.expandPath(true, remotePath);
        const exists = await this.internalExists(remotePath);
        this.logger.debug(`exists remotePath: ${remotePath} is ${exists}`);
        return exists
    }

    public async save(content: string, remotePath: string, isAttach: boolean = false): Promise<void> {
        remotePath = this.expandPath(true, remotePath);
        this.logger.debug(`saving content to remotePath: ${remotePath}, length: ${content.length}, isAttach: ${isAttach}`);
        const remoteDir = this.internalDirname(remotePath);

        if (isAttach) {
            if (await this.internalExists(remoteDir) === false) {
                await this.internalMkdir(remoteDir);
            }
            const result = await this.internalAttach(remotePath, content);
            if (false === result) {
                throw new Error("this.internalAttach doesn't support");
            }
        } else {
            const fileName = this.internalBasename(remotePath);
            const tempFileName = `temp_${uniqueString(4)}_${fileName}`;
            const localTempFileName = path.join(os.tmpdir(), tempFileName);
            const remoteTempFile = this.internalJoin(remoteDir, tempFileName);

            if (await this.internalExists(remotePath) === true) {
                await this.internalRemove(remotePath, false, false);
            }
            await fs.promises.writeFile(localTempFileName, content);
            await this.internalCopy(localTempFileName, remoteDir, false, false, true);
            await this.rename(remoteTempFile, fileName);
            await fs.promises.unlink(localTempFileName);
        }
    }

    public async copyFile(localPath: string, remotePath: string): Promise<void> {
        localPath = this.expandPath(false, localPath);
        remotePath = this.expandPath(true, remotePath);
        this.logger.debug(`copying file localPath: ${localPath} to remotePath: ${remotePath}`);
        await this.internalCopy(localPath, remotePath, false, false, true);
    }

    public async copyFileBack(remotePath: string, localPath: string): Promise<void> {
        localPath = this.expandPath(false, localPath);
        remotePath = this.expandPath(true, remotePath);
        this.logger.debug(`copy file remotePath: ${remotePath} to localPath: ${localPath}`);
154
        await this.internalCopy(remotePath, localPath, false, true, false);
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
    }

    public async removeFile(remotePath: string): Promise<void> {
        remotePath = this.expandPath(true, remotePath);
        this.logger.debug(`remove file remotePath: ${remotePath}`);
        await this.internalRemove(remotePath, false, false);
    }

    public joinPath(...paths: string[]): string {
        let fullPath = this.internalJoin(...paths);
        if (this.internalIsRelativePath(fullPath) === true && this.remoteRoot !== "") {
            fullPath = this.internalJoin(this.remoteRoot, fullPath);
        }
        return fullPath;
    }

    private expandPath(isRemote: boolean, ...paths: string[]): string {
        let normalizedPath: string;

        if (isRemote) {
            normalizedPath = this.joinPath(...paths);
        } else {
            normalizedPath = path.join(...paths);
            if (!path.isAbsolute(normalizedPath) && this.localRoot !== "") {
                normalizedPath = path.join(this.localRoot, normalizedPath);
            }
        }

        return normalizedPath;
    }
}