Skip to content

Commit 3bb4548

Browse files
authored
- add API for appending file content to an existing file (#224)
* - add API for appending file content to an existing file * - fix d.ts
1 parent 271bd01 commit 3bb4548

File tree

9 files changed

+905
-150
lines changed

9 files changed

+905
-150
lines changed

backendless.d.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -447,12 +447,25 @@ declare module Backendless {
447447

448448
let restUrl: string;
449449

450-
function saveFile(path: string, fileName: string, fileContent: Blob | string, overwrite?: boolean): Promise<boolean>;
450+
function saveFile(path: string, fileName: string, fileContent: Blob | Buffer | string, overwrite?: boolean): Promise<string>;
451451

452-
function upload(file: File, path: string, overwrite?: boolean): Promise<Object>;
453-
function upload(fileURL: string, path: string, overwrite?: boolean): Promise<Object>;
454452
// @ts-ignore - file has to be an instance of File in browser env and an instance of ReadStream in nodejs env
455-
function upload(readStream: ReadStream, path: string, overwrite?: boolean): Promise<Object>;
453+
function upload(readStream: ReadStream, path: string, overwrite?: boolean): Promise<{ fileURL: string }>;
454+
function upload(file: File, path: string, overwrite?: boolean): Promise<{ fileURL: string }>;
455+
function upload(fileURL: string, path: string, overwrite?: boolean): Promise<{ fileURL: string }>;
456+
457+
function append(filePath: string, fileURL: string): Promise<string>;
458+
function append(filePath: string, fileContent: Blob | Buffer | ArrayBuffer | number[]): Promise<string>;
459+
// @ts-ignore
460+
function append(filePath: string, readStream: ReadStream): Promise<string>;
461+
462+
function append(directoryPath: string, fileName: string, fileURL: string): Promise<string>;
463+
function append(directoryPath: string, fileName: string, fileContent: Blob | Buffer | ArrayBuffer | number[]): Promise<string>;
464+
// @ts-ignore
465+
function append(directoryPath: string, fileName: string, readStream: ReadStream): Promise<string>;
466+
467+
function appendText(directoryPath: string, fileName: string, fileContent: string): Promise<string>;
468+
function appendText(filePath: string, fileContent: string): Promise<string>;
456469

457470
function listing(path: string, pattern?: string, sub?: boolean, pageSize?: number, offset?: number): Promise<Object>;
458471

src/files/index.js

Lines changed: 95 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export default class Files {
2929
query.overwrite = overwrite
3030
}
3131

32-
filePath = FilesUtils.preventSlashInPath(filePath)
32+
filePath = FilesUtils.trimSlashesInPath(filePath)
3333
fileName = FilesUtils.sanitizeFileName(fileName)
3434

3535
return this.app.request.put({
@@ -47,7 +47,7 @@ export default class Files {
4747
query.overwrite = overwrite
4848
}
4949

50-
filePath = FilesUtils.preventSlashInPath(filePath)
50+
filePath = FilesUtils.trimSlashesInPath(filePath)
5151

5252
const pathTokens = FilesUtils.parseFilePath(filePath)
5353

@@ -66,7 +66,7 @@ export default class Files {
6666
}
6767

6868
return this.app.request.post({
69-
url : `${this.app.urls.filePath(filePath)}/${fileName || ''}`,
69+
url : `${this.app.urls.filePath(filePath)}/${fileName}`,
7070
query: query,
7171
data : {
7272
url: file
@@ -93,14 +93,102 @@ export default class Files {
9393
})
9494
}
9595

96+
async append(filePath, fileName, fileContent) {
97+
if (!filePath || typeof filePath !== 'string') {
98+
throw new Error('"filePath" must be provided and must be a string.')
99+
}
100+
101+
filePath = FilesUtils.trimSlashesInPath(filePath)
102+
103+
if (arguments.length === 2) {
104+
fileContent = fileName
105+
fileName = undefined
106+
107+
const pathTokens = FilesUtils.parseFilePath(filePath)
108+
109+
if (pathTokens.fileName) {
110+
filePath = pathTokens.filePath
111+
fileName = pathTokens.fileName
112+
}
113+
}
114+
115+
if (!fileName) {
116+
throw new Error('Can not resolve target file name')
117+
}
118+
119+
fileName = FilesUtils.sanitizeFileName(fileName)
120+
121+
if (typeof fileContent === 'string') {
122+
return this.app.request.post({
123+
url : `${this.app.urls.fileAppendPath(filePath)}/${fileName}`,
124+
data: {
125+
url: fileContent
126+
},
127+
})
128+
}
129+
130+
if (FilesUtils.isBytesArray(fileContent)) {
131+
fileContent = await FilesUtils.toBase64(fileContent)
132+
133+
return this.app.request.put({
134+
url : `${this.app.urls.fileAppendBinaryPath(filePath)}/${fileName}`,
135+
headers: { 'Content-Type': 'text/plain' },
136+
data : fileContent,
137+
})
138+
}
139+
140+
return this.app.request.post({
141+
url : `${this.app.urls.fileAppendPath(filePath)}/${fileName}`,
142+
form: {
143+
file: fileContent
144+
},
145+
})
146+
}
147+
148+
async appendText(filePath, fileName, textContent) {
149+
if (!filePath || typeof filePath !== 'string') {
150+
throw new Error('"filePath" must be provided and must be a string.')
151+
}
152+
153+
filePath = FilesUtils.trimSlashesInPath(filePath)
154+
155+
if (arguments.length === 2) {
156+
textContent = fileName
157+
fileName = undefined
158+
159+
const pathTokens = FilesUtils.parseFilePath(filePath)
160+
161+
if (pathTokens.fileName) {
162+
filePath = pathTokens.filePath
163+
fileName = pathTokens.fileName
164+
}
165+
}
166+
167+
if (!fileName) {
168+
throw new Error('Can not resolve target file name')
169+
}
170+
171+
if (typeof textContent !== 'string') {
172+
throw new Error('"textContent" must be a string')
173+
}
174+
175+
fileName = FilesUtils.sanitizeFileName(fileName)
176+
177+
return this.app.request.put({
178+
url : `${this.app.urls.fileAppendPath(filePath)}/${fileName}`,
179+
headers: { 'Content-Type': 'text/plain' },
180+
data : textContent,
181+
})
182+
}
183+
96184
async listing(filePath, pattern, sub, pagesize, offset) {
97185
const query = {}
98186

99187
if (!filePath || typeof filePath !== 'string') {
100188
throw new Error('"filePath" must be provided and must be a string.')
101189
}
102190

103-
filePath = FilesUtils.preventSlashInPath(filePath)
191+
filePath = FilesUtils.trimSlashesInPath(filePath)
104192

105193
if (pattern && typeof pattern === 'string') {
106194
query.pattern = pattern
@@ -181,7 +269,7 @@ export default class Files {
181269
throw new Error('"filePath" must be provided and must be a string.')
182270
}
183271

184-
filePath = FilesUtils.preventSlashInPath(filePath)
272+
filePath = FilesUtils.trimSlashesInPath(filePath)
185273

186274
return this.app.request.get({
187275
url : this.app.urls.filePath(filePath),
@@ -196,7 +284,7 @@ export default class Files {
196284
throw new Error('Directory "path" must be provided and must be a string.')
197285
}
198286

199-
directoryPath = FilesUtils.preventSlashInPath(directoryPath)
287+
directoryPath = FilesUtils.trimSlashesInPath(directoryPath)
200288

201289
return this.app.request.delete({
202290
url: this.app.urls.filePath(directoryPath),
@@ -212,7 +300,7 @@ export default class Files {
212300
throw new Error('Files Pattern must be provided and must be a string.')
213301
}
214302

215-
filesPath = FilesUtils.preventSlashInPath(filesPath)
303+
filesPath = FilesUtils.trimSlashesInPath(filesPath)
216304

217305
return this.app.request.get({
218306
url : this.app.urls.filePath(filesPath),

src/files/utils.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,33 @@
11
const FilesUtils = {
2+
3+
isBytesArray(data) {
4+
if (typeof Buffer !== 'undefined' && data instanceof Buffer) {
5+
return true
6+
}
7+
8+
if (typeof ArrayBuffer !== 'undefined' && data instanceof ArrayBuffer) {
9+
return true
10+
}
11+
12+
return Array.isArray(data)
13+
},
14+
215
ensureSlashInPath(path) {
316
return !path.startsWith('/') ? `/${path}` : path
417
},
518

6-
preventSlashInPath(path) {
7-
return (path && path.startsWith('/')) ? path.slice(1) : path
19+
trimSlashesInPath(path) {
20+
if (path) {
21+
if (path.startsWith('/')) {
22+
path = path.slice(1)
23+
}
24+
25+
if (path.endsWith('/')) {
26+
path = path.slice(0, path.length - 1)
27+
}
28+
}
29+
30+
return path
831
},
932

1033
parseFilePath(path) {

src/urls.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,14 @@ export default class Urls {
257257
return `${this.files()}/binary/${path}`
258258
}
259259

260+
fileAppendPath(path) {
261+
return `${this.files()}/append/${path}`
262+
}
263+
264+
fileAppendBinaryPath(path) {
265+
return `${this.files()}/append/binary/${path}`
266+
}
267+
260268
//users
261269
users() {
262270
return `${this.root()}/users`

test/e2e/specs/files.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,44 @@ describe('Backendless.Files', function() {
303303
})
304304
})
305305

306+
describe('Append', function() {
307+
it('appends text content', async () => {
308+
const fileDir = 'test-append'
309+
const fileName = 'buffer-file.txt'
310+
const filePath = `${fileDir}/${fileName}`
311+
312+
const fileURL1 = await Files.append(fileDir, fileName, Buffer.from('aaa'))
313+
const fileURL2 = await Files.append(fileDir, fileName, Buffer.from('bbb'))
314+
const fileURL3 = await Files.append(fileDir, fileName, Buffer.from('ccc'))
315+
316+
expect(fileURL1).to.be.equal(fileURL2)
317+
expect(fileURL2).to.be.equal(fileURL3)
318+
319+
const fileContent = await readFile(filePath)
320+
321+
expect(fileContent).to.eql('aaabbbccc')
322+
})
323+
})
324+
325+
describe('Append Text', function() {
326+
it('appends text content', async () => {
327+
const fileDir = 'test-append'
328+
const fileName = 'text-file.txt'
329+
const filePath = `${fileDir}/${fileName}`
330+
331+
const fileURL1 = await Files.appendText(fileDir, fileName, 'aaa')
332+
const fileURL2 = await Files.appendText(fileDir, fileName, 'bbb')
333+
const fileURL3 = await Files.appendText(fileDir, fileName, 'ccc')
334+
335+
expect(fileURL1).to.be.equal(fileURL2)
336+
expect(fileURL2).to.be.equal(fileURL3)
337+
338+
const fileContent = await readFile(filePath)
339+
340+
expect(fileContent).to.eql('aaabbbccc')
341+
})
342+
})
343+
306344
describe('Upload', function() {
307345
it('Upload file', function() {
308346
if (typeof File !== 'undefined') {

0 commit comments

Comments
 (0)