index.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. const request = require('request')
  2. // @ts-ignore
  3. const purest = require('purest')({ request })
  4. const logger = require('../../logger')
  5. const adapter = require('./adapter')
  6. const AuthError = require('../error')
  7. const DRIVE_FILE_FIELDS = 'kind,id,name,mimeType,ownedByMe,permissions(role,emailAddress),size,modifiedTime,iconLink,thumbnailLink,teamDriveId'
  8. const DRIVE_FILES_FIELDS = `kind,nextPageToken,incompleteSearch,files(${DRIVE_FILE_FIELDS})`
  9. const TEAM_DRIVE_FIELDS = 'teamDrives(kind,id,name,backgroundImageLink)'
  10. class Drive {
  11. constructor (options) {
  12. this.authProvider = options.provider = Drive.authProvider
  13. options.alias = 'drive'
  14. options.version = 'v3'
  15. this.client = purest(options)
  16. }
  17. static get authProvider () {
  18. return 'google'
  19. }
  20. list (options, done) {
  21. const directory = options.directory || 'root'
  22. const query = options.query || {}
  23. let teamDrivesPromise = Promise.resolve(undefined)
  24. const shouldListTeamDrives = directory === 'root' && !query.nextPageToken
  25. if (shouldListTeamDrives) {
  26. teamDrivesPromise = new Promise((resolve) => {
  27. this.client
  28. .query()
  29. .get('teamdrives')
  30. .where({ fields: TEAM_DRIVE_FIELDS })
  31. .auth(options.token)
  32. .request((err, resp) => {
  33. if (err) {
  34. logger.error(err, 'provider.drive.teamDrive.error')
  35. return
  36. }
  37. resolve(resp)
  38. })
  39. })
  40. }
  41. let where = {
  42. fields: DRIVE_FILES_FIELDS,
  43. pageToken: query.nextPageToken,
  44. q: `'${directory}' in parents and trashed=false`,
  45. includeItemsFromAllDrives: true,
  46. supportsAllDrives: true
  47. }
  48. const filesPromise = new Promise((resolve, reject) => {
  49. this.client
  50. .query()
  51. .get('files')
  52. .where(where)
  53. .auth(options.token)
  54. .request((err, resp) => {
  55. if (err) {
  56. reject(err)
  57. return
  58. }
  59. resolve(resp)
  60. })
  61. })
  62. Promise.all([teamDrivesPromise, filesPromise])
  63. .then(
  64. ([teamDrives, filesResponse]) => {
  65. if (filesResponse.statusCode !== 200) {
  66. const err = this._error(null, filesResponse)
  67. logger.error(err, 'provider.drive.list.error')
  68. done(err)
  69. return
  70. }
  71. const returnData = this.adaptData(
  72. filesResponse.body,
  73. teamDrives && teamDrives.body,
  74. options.uppy,
  75. directory,
  76. query
  77. )
  78. done(null, returnData)
  79. },
  80. (reqErr) => {
  81. logger.error(reqErr, 'provider.drive.list.error')
  82. done(reqErr)
  83. }
  84. )
  85. }
  86. stats ({ id, token }, done) {
  87. return this.client
  88. .query()
  89. .get(`files/${id}`)
  90. .where({ fields: DRIVE_FILE_FIELDS, supportsTeamDrives: true })
  91. .auth(token)
  92. .request(done)
  93. }
  94. download ({ id, token }, onData) {
  95. return this.client
  96. .query()
  97. .get(`files/${id}`)
  98. .where({ alt: 'media', supportsTeamDrives: true })
  99. .auth(token)
  100. .request()
  101. .on('data', onData)
  102. .on('end', () => onData(null))
  103. .on('error', (err) => {
  104. logger.error(err, 'provider.drive.download.error')
  105. })
  106. }
  107. thumbnail ({ id, token }, done) {
  108. return this.stats({ id, token }, (err, resp, body) => {
  109. if (err || resp.statusCode !== 200) {
  110. err = this._error(err, resp)
  111. logger.error(err, 'provider.drive.thumbnail.error')
  112. return done(err)
  113. }
  114. done(null, body.thumbnailLink ? request(body.thumbnailLink) : null)
  115. })
  116. }
  117. size ({ id, token }, done) {
  118. return this.stats({ id, token }, (err, resp, body) => {
  119. if (err || resp.statusCode !== 200) {
  120. err = this._error(err, resp)
  121. logger.error(err, 'provider.drive.size.error')
  122. return done(err)
  123. }
  124. done(null, parseInt(body.size))
  125. })
  126. }
  127. adaptData (res, teamDrivesResp, uppy, directory, query) {
  128. const adaptItem = (item) => ({
  129. isFolder: adapter.isFolder(item),
  130. icon: adapter.getItemIcon(item),
  131. name: adapter.getItemName(item),
  132. mimeType: adapter.getMimeType(item),
  133. id: adapter.getItemId(item),
  134. thumbnail: uppy.buildURL(adapter.getItemThumbnailUrl(item), true),
  135. requestPath: adapter.getItemRequestPath(item),
  136. modifiedDate: adapter.getItemModifiedDate(item),
  137. size: adapter.getItemSize(item),
  138. custom: {
  139. isTeamDrive: adapter.isTeamDrive(item)
  140. }
  141. })
  142. const items = adapter.getItemSubList(res)
  143. const teamDrives = teamDrivesResp ? teamDrivesResp.teamDrives || [] : []
  144. const adaptedItems = teamDrives.concat(items).map(adaptItem)
  145. return {
  146. username: adapter.getUsername(res),
  147. items: adaptedItems,
  148. nextPagePath: adapter.getNextPagePath(res, query, directory)
  149. }
  150. }
  151. _error (err, resp) {
  152. if (resp) {
  153. const errMsg = `request to ${this.authProvider} returned ${resp.statusCode}`
  154. return resp.statusCode === 401 ? new AuthError() : new Error(errMsg)
  155. }
  156. return err
  157. }
  158. }
  159. module.exports = Drive