index.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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. const teamDrive = query.teamDrive
  24. let listDone = false
  25. let teamDrivesDone = false
  26. let teamDrives
  27. let listResponse
  28. let reqErr
  29. const finishReq = () => {
  30. if (reqErr || listResponse.statusCode !== 200) {
  31. const err = this._error(reqErr, listResponse)
  32. logger.error(err, 'provider.drive.list.error')
  33. done(err)
  34. } else {
  35. done(null, this.adaptData(listResponse.body, teamDrives ? teamDrives.body : null, options.uppy))
  36. }
  37. }
  38. if (directory === 'root') {
  39. // fetch a list of all Team Drives which the user can access.
  40. this.client
  41. .query()
  42. .get('teamdrives')
  43. .where({ fields: TEAM_DRIVE_FIELDS })
  44. .auth(options.token)
  45. .request((err, resp) => {
  46. if (err) {
  47. logger.error(err, 'provider.drive.teamDrive.error')
  48. }
  49. teamDrivesDone = true
  50. teamDrives = resp
  51. if (listDone) {
  52. finishReq()
  53. }
  54. })
  55. }
  56. let where = {
  57. fields: DRIVE_FILES_FIELDS,
  58. q: `'${directory}' in parents and trashed=false`
  59. }
  60. if (teamDrive) {
  61. // Team Drives require several extra parameters in order to work.
  62. where.supportsTeamDrives = true
  63. where.includeTeamDriveItems = true
  64. where.teamDriveId = directory
  65. where.corpora = 'teamDrive'
  66. }
  67. this.client
  68. .query()
  69. .get('files')
  70. .where(where)
  71. .auth(options.token)
  72. .request((err, resp) => {
  73. listDone = true
  74. listResponse = resp
  75. reqErr = err
  76. if (teamDrivesDone || directory !== 'root') {
  77. finishReq()
  78. }
  79. })
  80. }
  81. stats ({ id, token }, done) {
  82. return this.client
  83. .query()
  84. .get(`files/${id}`)
  85. .where({ fields: DRIVE_FILE_FIELDS, supportsTeamDrives: true })
  86. .auth(token)
  87. .request(done)
  88. }
  89. download ({ id, token }, onData) {
  90. return this.client
  91. .query()
  92. .get(`files/${id}`)
  93. .where({ alt: 'media', supportsTeamDrives: true })
  94. .auth(token)
  95. .request()
  96. .on('data', onData)
  97. .on('end', () => onData(null))
  98. .on('error', (err) => {
  99. logger.error(err, 'provider.drive.download.error')
  100. })
  101. }
  102. thumbnail ({id, token}, done) {
  103. return this.stats({id, token}, (err, resp, body) => {
  104. if (err || resp.statusCode !== 200) {
  105. err = this._error(err, resp)
  106. logger.error(err, 'provider.drive.thumbnail.error')
  107. return done(err)
  108. }
  109. done(null, body.thumbnailLink ? request(body.thumbnailLink) : null)
  110. })
  111. }
  112. size ({id, token}, done) {
  113. return this.stats({ id, token }, (err, resp, body) => {
  114. if (err || resp.statusCode !== 200) {
  115. err = this._error(err, resp)
  116. logger.error(err, 'provider.drive.size.error')
  117. return done(err)
  118. }
  119. done(null, parseInt(body.size))
  120. })
  121. }
  122. adaptData (res, teamDrivesResp, uppy) {
  123. const data = { username: adapter.getUsername(res), items: [] }
  124. const items = adapter.getItemSubList(res)
  125. const teamDrives = teamDrivesResp ? teamDrivesResp.teamDrives || [] : []
  126. items.concat(teamDrives).forEach((item) => {
  127. data.items.push({
  128. isFolder: adapter.isFolder(item),
  129. icon: adapter.getItemIcon(item),
  130. name: adapter.getItemName(item),
  131. mimeType: adapter.getMimeType(item),
  132. id: adapter.getItemId(item),
  133. thumbnail: uppy.buildURL(adapter.getItemThumbnailUrl(item), true),
  134. requestPath: adapter.getItemRequestPath(item),
  135. modifiedDate: adapter.getItemModifiedDate(item),
  136. custom: {
  137. isTeamDrive: adapter.isTeamDrive(item)
  138. }
  139. })
  140. })
  141. data.nextPagePath = null
  142. return data
  143. }
  144. _error (err, resp) {
  145. if (resp) {
  146. const errMsg = `request to ${this.authProvider} returned ${resp.statusCode}`
  147. return resp.statusCode === 401 ? new AuthError() : new Error(errMsg)
  148. }
  149. return err
  150. }
  151. }
  152. module.exports = Drive