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

Typescript wrong types on drive.files.get method return value #1768

Closed
mercteil opened this issue Jul 24, 2019 · 4 comments
Closed

Typescript wrong types on drive.files.get method return value #1768

mercteil opened this issue Jul 24, 2019 · 4 comments
Assignees
Labels
priority: p2 Moderately-important priority. Fix may not be included in next release. 🚨 This issue needs some love. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. TypeScript

Comments

@mercteil
Copy link

mercteil commented Jul 24, 2019

Environment details

  • Node.js version: 10.xx
  • npm version: 6.xx
  • googleapis version: 41.0.1 (drive v3, JWT auth)

Steps to reproduce

  1. Take any accessible file id
  2. Set responseType: 'stream'

I am trying to download files (a JPG image in my case) from a GDrive with a service account. Authentication etc. works fine, but it seems there is an issue with the types when it comes to the drive.files.get method.

This request below is written in JavaScript and works fine, although the original google docs are not up to date this is the current workaround:

const dest = fs.createWriteStream('picture.jpg');
await drive.files.get(
    {
      fileId: '<file id here>',
      alt: 'media',
    },
    {
      responseType: 'stream',
    },
    (err, res) => {
      res.data
        .on('end', () => {
          console.log('Done');
        })
        .on('error', error => {
          console.log('Error during download', error);
        })
        .pipe(dest);
    }
  );

=> works fine, jpg is created and viewable


Using the same code working with Typescript:

const dest = fs.createWriteStream('photo.jpg');
await drive.files.get(
    {
      fileId: '<file id here>',
      alt: 'media',
    },
    {
      responseType: 'stream',
    },
    (err, res): void => {
      if (res) {
        res.data
          .on('end', (): void => { //Property 'on' does not exist on type 'Schema$File'. 
            console.log('Done');
          })
          .on('error', (error): void => {
            console.log('Error during download', error);
          })
          .pipe(dest);
      }
    }
  );

Receiving the following error:
Property 'on' does not exist on type 'Schema$File'.

res.data should be of type 'Stream' in this request configuration.


Finally I started to try&error around this issue and wanted to first wait for the Promise to finish and then write it to file without responseType: stream

  await drive.files
    .get({
      fileId: '<file id here>',
      alt: 'media',
    })
    .then((res): void => {
      console.log(typeof res.data); // string
      Buffer.from(res.data); // Argument of type 'Schema$File' is not assignable to parameter of type 'string'.
    });

At this point the only workaround for me is a crappy splitting of code into a JS module to fetch files and import the module in my TS code, because TS is all over the place with wrong types for the drive API.


And finally what does drive.files.get => res.data return? Is it a binary string? Is it something else? No explanation in the docs at all.

@mercteil
Copy link
Author

mercteil commented Jul 24, 2019

The problem is in the types here:

Return should always include Stream, ArrayBuffer, Blob alternative.
When sending request with alt:media option file contents are NOT received from type Schema$File

https://github.com/googleapis/google-api-nodejs-client/blob/master/src/apis/drive/v3.ts

   get(
      params?: Params$Resource$Files$Get,
      options?: MethodOptions
    ): GaxiosPromise<Schema$File>;
    get(
      params: Params$Resource$Files$Get,
      options: MethodOptions | BodyResponseCallback<Schema$File>,
      callback: BodyResponseCallback<Schema$File>
    ): void;
    get(
      params: Params$Resource$Files$Get,
      callback: BodyResponseCallback<Schema$File>
    ): void;
    get(callback: BodyResponseCallback<Schema$File>): void;
    get(
      paramsOrCallback?:
        | Params$Resource$Files$Get
        | BodyResponseCallback<Schema$File>,
      optionsOrCallback?: MethodOptions | BodyResponseCallback<Schema$File>,
      callback?: BodyResponseCallback<Schema$File>
    ): void | GaxiosPromise<Schema$File>

How to work with the file contents, since the docs do not provide any further details:

  • you MUST set the responseType: 'arraybuffer' | 'stream' | 'blob' to work with the binaries (default is JSON and you receive encoded JSON as file contents if you do not set anything)
  • if you use Typescript you have to overwrite the response type res:any
 const dest = fs.createWriteStream('p5.jpg');

 await drive.files.get(
   {
     fileId: '<file id>',
     alt: 'media',
   },
   {
     responseType: 'arraybuffer', // MUST set responseType (eg. Buffer)
   },
   (err, res: any): void => { // set res: any to overwrite the type
     if (res) {
       console.log(res.data); // arraybuffer
       dest.write(Buffer.from(res.data));
  }
})

@bcoe bcoe added type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. TypeScript priority: p2 Moderately-important priority. Fix may not be included in next release. labels Jul 29, 2019
@JustinBeckwith
Copy link
Contributor

Greetings folks! This ain't perfect, but I honestly don't know if it's something we can directly fix. The good news is that - you don't need to cast to any here - you can just cast to a stream:

const drive = google.drive({ version: "v3"});
const dest = fs.createWriteStream('picture.jpg');
const res = await drive.files.get({
  fileId: '<file id here>',
  alt: 'media',
}, {
  responseType: 'stream'
});
const streamy = res.data as Readable;
streamy.pipe(dest);

Does that cast work? The reason this is hard - I can't quite seem to get the return type to differ based on the specific values within an object. I will keep poking at it, but this should be a pretty simple work around.

@JustinBeckwith JustinBeckwith self-assigned this Dec 4, 2019
@yoshi-automation yoshi-automation added the 🚨 This issue needs some love. label Jan 20, 2020
@hc3
Copy link

hc3 commented May 1, 2020

me salvou

@ScottG489
Copy link

Hey @JustinBeckwith. Just wanted to say great work with this lib!

I had a quick question. What would I cast to if I were specifying a responseType of 'blob'?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority: p2 Moderately-important priority. Fix may not be included in next release. 🚨 This issue needs some love. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. TypeScript
Projects
None yet
Development

No branches or pull requests

6 participants