Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File.save() with a Uint8Array fails #2478

Closed
aiden-jeffrey opened this issue Jun 6, 2024 · 9 comments · Fixed by #2480
Closed

File.save() with a Uint8Array fails #2478

aiden-jeffrey opened this issue Jun 6, 2024 · 9 comments · Fixed by #2480
Assignees
Labels
api: storage Issues related to the googleapis/nodejs-storage API. priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.

Comments

@aiden-jeffrey
Copy link

aiden-jeffrey commented Jun 6, 2024

When saving a UintArray8, we see the error The "chunk" argument must be of type string or an instance of Buffer or Uint8Array.. This bug is only present in 7.0.0 onwards. I have to cast it to a buffer for the save to succeed, which wasn't necessary on 6.x.x.

Environment details

  • OS: Ubuntu 23.04
  • Node.js version: v18.13.0
  • npm version: 8.19.3
  • @google-cloud/storage version: 7.11.1

Steps to reproduce

  1. Bucket.file("some-file.bin").save(new Uint8Array([1,2,3,4,5]))
  2. See error:
TypeError [ERR_INVALID_ARG_TYPE]: The "chunk" argument must be of type string or an instance of Buffer or Uint8Array. 
Received type number (20)
at new NodeError (node:internal/errors:400:5)
at _write (node:internal/streams/writable:315:13)
at Writable.write (node:internal/streams/writable:337:10)
at pump (node:internal/streams/pipeline:130:21)
at processTicksAndRejections (node:internal/process/task_queues:95:5) {
  code: 'ERR_INVALID_ARG_TYPE'
}
error Command failed with exit code 1.
@aiden-jeffrey aiden-jeffrey added priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. labels Jun 6, 2024
@product-auto-label product-auto-label bot added the api: storage Issues related to the googleapis/nodejs-storage API. label Jun 6, 2024
@ddelgrosso1 ddelgrosso1 self-assigned this Jun 6, 2024
@ddelgrosso1
Copy link
Contributor

@aiden-jeffrey did you also update node versions as part of the move from 6.x to 7.x? Looking at the signature for file.save in 6.9.3 it was defined as only accepting string | Buffer. We added PipelineSource in 7.x as another option.

If you updated Node versions perhaps there used to be an automatic conversion that was changed.

@aiden-jeffrey
Copy link
Author

No. I didn't change node version. We upgraded to 7.11.1 (following direction here #2476), and saw the bug. Then I bisected to work out which was the first release to exhibit - 7.0.0.

@sidsethupathi
Copy link

sidsethupathi commented Jun 6, 2024

If it isn't clear, Aiden and I work together.

I also don't think 7.0.0 is the first bad commit. I checked out this repo, bisected the commits, and landed on c0d9d58 to be the commit that starts failing when passing in a Uint8Array, always using Node v18.13.0 during the bisect.

EDIT:
More specifically, it looks like it's this conditional. Applying this diff makes the case mentioned in the issue work:

diff --git a/src/file.ts b/src/file.ts
index 6eed2ca..dc260a0 100644
--- a/src/file.ts
+++ b/src/file.ts
@@ -3679,7 +3679,7 @@ class File extends ServiceObject<File, FileMetadata> {
             reject(err);
           };
 
-          if (typeof data === 'string' || Buffer.isBuffer(data)) {
+          if (typeof data === 'string' || Buffer.isBuffer(data) || data instanceof Uint8Array) {
             writable
               .on('error', handleError)
               .on('finish', () => resolve())

@ddelgrosso1
Copy link
Contributor

@sidsethupathi beat me to it. I was just looking at those lines. It looks like Uint8Array worked previously because despite the TypeScript signature saying string | Buffer we never actually verified that data was actually one of those. Let me take a look at extending the signature and checks to accept further types.

@sidsethupathi
Copy link

The interesting thing is that we are using TypeScript so we'll look into why TS didn't yell at us for trying to pass in a Uint8Array into the File.save method.

@ddelgrosso1
Copy link
Contributor

ddelgrosso1 commented Jun 6, 2024

When I tried in TypeScript just now, it wouldn't accept a UInt8Array.

typescript: 5.3.3
@types/node: 20.11.20
@google-cloud/storage@6.9.3

error TS2345: Argument of type 'Uint8Array' is not assignable to parameter of type 'SaveData'.
  Type 'Uint8Array' is missing the following properties from type 'Buffer': write, toJSON, equals, compare, and 66 more.

   const resp = await b.file('test').save(new Uint8Array([1,2,3,4,5]));

@sidsethupathi
Copy link

sidsethupathi commented Jun 6, 2024

Yes, we have some abstract classes on top of our GCS invocations so I think something got messed up there.

In short, we have an abstract class

abstract class Writer {
  abstract saveBinary(data: Buffer | Uint8Array);
}

and then a GCS specific implementation

class GCSWriter extends Writer {
  saveBinary(data: Buffer) {
    // call File.save(data) here
  }
}

I think since Buffer extends Uint8Array, TS allows GCSWriter.saveBinary(data: Buffer) to satisfy Writer.saveBinary(data: Buffer | Uint8Array) because all Buffers will be Uint8Arrays, but then that borks the actual implementation because File.save doesn't want to take a Uint8Array.

EDIT:

Where this all falls apart is that that we never call GCSWriter.saveBinary directly, it all happens from within the abstract Writer class

abstract class Writer {
  abstract saveBinary(data: Buffer | Uint8Array);

  transform(foo: Foo) {
    const data: Uint8Array = convertFooToUint8Array(foo);
    this.saveBinary(data);
  }
}

const gcsWriter = new GCSWriter();
gcsWriter.transform(foo);

@ddelgrosso1
Copy link
Contributor

@sidsethupathi thank you for your investigation. I've opened a PR to add support for Uint8Array.

@aiden-jeffrey
Copy link
Author

aiden-jeffrey commented Jun 7, 2024

@sidsethupathi thank you for your investigation. I've opened a PR to add support for Uint8Array.

Thank you! I was sure I'd checked the type definition in SaveData, but apparently I didn't.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: storage Issues related to the googleapis/nodejs-storage API. priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants