Skip to content
This repository was archived by the owner on Mar 3, 2026. It is now read-only.

Commit 87c3f41

Browse files
authored
fix: File#save iterable fixes (#2293)
* fix: File#save iterable fixes * fix: if logic * test: remove unneccesary test * fix: return on save-stream-bail * refactor: remove non-reachable case
1 parent 97e6f2d commit 87c3f41

2 files changed

Lines changed: 5 additions & 79 deletions

File tree

src/file.ts

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,6 @@ export enum FileExceptionMessages {
463463
UPLOAD_MISMATCH = `The uploaded data did not match the data from the server.
464464
As a precaution, the file has been deleted.
465465
To be sure the content is the same, you should try uploading the file again.`,
466-
STREAM_NOT_READABLE = 'Stream must be readable.',
467466
}
468467

469468
/**
@@ -3644,20 +3643,6 @@ class File extends ServiceObject<File, FileMetadata> {
36443643
}
36453644
const returnValue = retry(
36463645
async (bail: (err: Error) => void) => {
3647-
if (data instanceof Readable) {
3648-
// Make sure any pending async readable operations are finished before
3649-
// attempting to check if the stream is readable.
3650-
await new Promise(resolve => setImmediate(resolve));
3651-
3652-
if (!data.readable || data.destroyed) {
3653-
// Calling pipeline() with a non-readable stream will result in the
3654-
// callback being called without an error, and no piping taking
3655-
// place. In that case, file.save() would appear to succeed, but
3656-
// nothing would be uploaded.
3657-
return bail(new Error(FileExceptionMessages.STREAM_NOT_READABLE));
3658-
}
3659-
}
3660-
36613646
return new Promise<void>((resolve, reject) => {
36623647
if (maxRetries === 0) {
36633648
this.storage.retryOptions.autoRetry = false;
@@ -3670,13 +3655,13 @@ class File extends ServiceObject<File, FileMetadata> {
36703655

36713656
const handleError = (err: Error) => {
36723657
if (
3673-
!this.storage.retryOptions.autoRetry ||
3674-
!this.storage.retryOptions.retryableErrorFn!(err)
3658+
this.storage.retryOptions.autoRetry &&
3659+
this.storage.retryOptions.retryableErrorFn!(err)
36753660
) {
3676-
bail(err);
3661+
return reject(err);
36773662
}
36783663

3679-
reject(err);
3664+
return bail(err);
36803665
};
36813666

36823667
if (typeof data === 'string' || Buffer.isBuffer(data)) {
@@ -3687,17 +3672,10 @@ class File extends ServiceObject<File, FileMetadata> {
36873672
} else {
36883673
pipeline(data, writable, err => {
36893674
if (err) {
3690-
// If data is not a valid PipelineSource, then pipeline will
3691-
// fail without destroying the writable stream. If data is a
3692-
// PipelineSource that yields invalid chunks (e.g. a stream in
3693-
// object mode or an iterable that does not yield Buffers or
3694-
// strings), then pipeline will destroy the writable stream.
3695-
if (!writable.destroyed) writable.destroy();
3696-
36973675
if (typeof data !== 'function') {
36983676
// Only PipelineSourceFunction can be retried. Async-iterables
36993677
// and Readable streams can only be consumed once.
3700-
bail(err);
3678+
return bail(err);
37013679
}
37023680

37033681
handleError(err);

test/file.ts

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4365,31 +4365,6 @@ describe('File', () => {
43654365
}
43664366
});
43674367

4368-
it('Destroyed Readable upload should throw', async () => {
4369-
const options = {resumable: false};
4370-
4371-
file.createWriteStream = () => {
4372-
throw new Error('unreachable');
4373-
};
4374-
try {
4375-
const readable = new Readable({
4376-
read() {
4377-
this.push(DATA);
4378-
this.push(null);
4379-
},
4380-
});
4381-
4382-
readable.destroy();
4383-
4384-
await file.save(readable, options);
4385-
} catch (e) {
4386-
assert.strictEqual(
4387-
(e as Error).message,
4388-
FileExceptionMessages.STREAM_NOT_READABLE
4389-
);
4390-
}
4391-
});
4392-
43934368
it('should save a generator with no error', done => {
43944369
const options = {resumable: false};
43954370
file.createWriteStream = () => {
@@ -4439,33 +4414,6 @@ describe('File', () => {
44394414
});
44404415
});
44414416

4442-
it('should error on invalid async iterator data', done => {
4443-
const options = {resumable: false};
4444-
file.createWriteStream = () => {
4445-
const writeStream = new PassThrough();
4446-
let errorCalled = false;
4447-
writeStream.on('error', () => {
4448-
errorCalled = true;
4449-
});
4450-
writeStream.on('finish', () => {
4451-
assert.ok(errorCalled);
4452-
});
4453-
return writeStream;
4454-
};
4455-
4456-
const generator = async function* () {
4457-
yield {thisIsNot: 'a buffer or a string'};
4458-
};
4459-
4460-
file.save(generator(), options, (err: Error) => {
4461-
assert.strictEqual(
4462-
err.message,
4463-
'The "chunk" argument must be of type string or an instance of Buffer or Uint8Array. Received an instance of Object'
4464-
);
4465-
done();
4466-
});
4467-
});
4468-
44694417
it('buffer upload should retry on first failure', async () => {
44704418
const options = {
44714419
resumable: false,

0 commit comments

Comments
 (0)