Procházet zdrojové kódy

refactor: migrate provide data formatting to backend

Ifedapo Olarewaju před 6 roky
rodič
revize
1b59a6f4fb

+ 1 - 1
packages/@uppy/companion/src/server/controllers/authorized.js

@@ -18,7 +18,7 @@ function authorized (req, res) {
   }
 
   const token = uppy.providerTokens[providerName]
-  uppy.provider.list({ token }, (err, response, body) => {
+  uppy.provider.list({ token, uppy }, (err, response, body) => {
     const notAuthenticated = Boolean(err)
     if (notAuthenticated) {
       logger.debug(`${providerName} failed authorizarion test err:${err}`, 'provider.auth.check')

+ 2 - 5
packages/@uppy/companion/src/server/controllers/list.js

@@ -2,11 +2,8 @@ function list ({ query, params, uppy }, res, next) {
   const providerName = params.providerName
   const token = uppy.providerTokens[providerName]
 
-  uppy.provider.list({ token, directory: params.id, query }, (err, resp, body) => {
-    if (err) {
-      return next(err)
-    }
-    return res.json(body)
+  uppy.provider.list({ uppy, token, directory: params.id, query }, (err, data) => {
+    return err ? next(err) : res.json(data)
   })
 }
 

+ 8 - 6
packages/@uppy/companion/src/server/provider/drive/adapter.js

@@ -11,7 +11,7 @@ exports.getUsername = (data) => {
 }
 
 exports.isFolder = (item) => {
-  return item.mimeType === 'application/vnd.google-apps.folder'
+  return item.mimeType === 'application/vnd.google-apps.folder' || item.kind === 'drive#teamDrive'
 }
 
 exports.getItemData = (item) => {
@@ -19,12 +19,15 @@ exports.getItemData = (item) => {
 }
 
 exports.getItemIcon = (item) => {
+  if (item.kind === 'drive#teamDrive') {
+    return item.backgroundImageLink + '=w16-h16-n'
+  }
   return item.iconLink
 }
 
 exports.getItemSubList = (item) => {
   return item.files.filter((i) => {
-    return this.isFolder(i) || !i.mimeType.startsWith('application/vnd.google')
+    return exports.isFolder(i) || !i.mimeType.startsWith('application/vnd.google')
   })
 }
 
@@ -44,11 +47,10 @@ exports.getItemRequestPath = (item) => {
   // If it's from a Team Drive, add the Team Drive ID as a query param.
   // The server needs the Team Drive ID to list files in a Team Drive folder.
   if (item.teamDriveId) {
-    item.id += `?teamDriveId=${item.teamDriveId}`
-    delete item.teamDriveId
+    return item.id + `?teamDriveId=${item.teamDriveId}`
   }
 
-  return this.getItemId(item)
+  return item.id
 }
 
 exports.getItemModifiedDate = (item) => {
@@ -56,5 +58,5 @@ exports.getItemModifiedDate = (item) => {
 }
 
 exports.getItemThumbnailUrl = (item) => {
-  return `${this.opts.serverUrl}/${this.GoogleDrive.id}/thumbnail/${this.getItemRequestPath(item)}`
+  return `/drive/thumbnail/${exports.getItemRequestPath(item)}`
 }

+ 76 - 31
packages/@uppy/companion/src/server/provider/drive/index.js

@@ -2,9 +2,11 @@ const request = require('request')
 // @ts-ignore
 const purest = require('purest')({ request })
 const logger = require('../../logger')
+const adapter = require('./adapter')
 const DRIVE_FILE_FIELDS = 'kind,id,name,mimeType,ownedByMe,permissions(role,emailAddress),size,modifiedTime,iconLink,thumbnailLink,teamDriveId'
 const DRIVE_FILES_FIELDS = `kind,nextPageToken,incompleteSearch,files(${DRIVE_FILE_FIELDS})`
 const TEAM_DRIVE_FIELDS = 'teamDrives(kind,id,name,backgroundImageLink)'
+
 /**
  * @class
  * @implements {Provider}
@@ -24,43 +26,67 @@ class Drive {
 
   list (options, done) {
     const directory = options.directory || 'root'
-    const trashed = options.trashed || false
     const query = options.query || {}
-    const listTeamDrives = query.listTeamDrives || 'false'
-    const teamDriveId = query.teamDriveId || false
+    const teamDrive = query.teamDrive
+    let listDone = false
+    let teamDrivesDone = false
+    let teamDrives
+    let listResponse
+    let reqErr
+    const finishReq = () => {
+      if (reqErr) {
+        done(reqErr)
+      } else if (listResponse.statusCode !== 200) {
+        done(new Error(`request to ${this.authProvider} returned ${listResponse.statusCode}`))
+      } else {
+        done(null, this.adaptData(listResponse.body, teamDrives ? teamDrives.body : null, options.uppy))
+      }
+    }
 
-    if (listTeamDrives === 'true') {
-      // Just return a list of all Team Drives which the user can access.
-      return this.client
+    if (directory === 'root') {
+      // fetch a list of all Team Drives which the user can access.
+      this.client
         .query()
         .get('teamdrives')
         .where({ fields: TEAM_DRIVE_FIELDS })
         .auth(options.token)
-        .request(done)
-    } else {
-      let where = {
-        fields: DRIVE_FILES_FIELDS,
-        q: `'${directory}' in parents and trashed=${trashed}`
-      }
-      if (teamDriveId) {
-        // Team Drives require several extra parameters in order to work.
-        where.supportsTeamDrives = true
-        where.includeTeamDriveItems = true
-        where.teamDriveId = teamDriveId
-        where.corpora = 'teamDrive'
-        if (directory === 'root') {
-          // To list the top-level contents of a Team Drive, filter for the Team Drive id as a parent.
-          where.q = `'${teamDriveId}' in parents and trashed=${trashed}`
-        }
-      }
+        .request((err, resp) => {
+          if (err) {
+            logger.error(err, 'provider.drive.teamDrive.error')
+          }
+          teamDrivesDone = true
+          teamDrives = resp
+          if (listDone) {
+            finishReq()
+          }
+        })
+    }
 
-      return this.client
-        .query()
-        .get('files')
-        .where(where)
-        .auth(options.token)
-        .request(done)
+    let where = {
+      fields: DRIVE_FILES_FIELDS,
+      q: `'${directory}' in parents and trashed=false`
     }
+    if (teamDrive) {
+      // Team Drives require several extra parameters in order to work.
+      where.supportsTeamDrives = true
+      where.includeTeamDriveItems = true
+      where.teamDriveId = directory
+      where.corpora = 'teamDrive'
+    }
+
+    this.client
+      .query()
+      .get('files')
+      .where(where)
+      .auth(options.token)
+      .request((err, resp) => {
+        listDone = true
+        listResponse = resp
+        reqErr = err
+        if (teamDrivesDone || directory !== 'root') {
+          finishReq()
+        }
+      })
   }
 
   stats ({ id, token }, done) {
@@ -106,8 +132,27 @@ class Drive {
     })
   }
 
-  adaptData () {
-    
+  adaptData (res, teamDrivesResp, uppy) {
+    const data = { username: adapter.getUsername(res), items: [] }
+    const items = adapter.getItemSubList(res)
+    const teamDrives = teamDrivesResp ? teamDrivesResp.teamDrives || [] : []
+    items.concat(teamDrives).forEach((item) => {
+      data.items.push({
+        isFolder: adapter.isFolder(item),
+        icon: adapter.getItemIcon(item),
+        name: adapter.getItemName(item),
+        mimeType: adapter.getMimeType(item),
+        id: adapter.getItemId(item),
+        thumbnail: uppy.buildURL(adapter.getItemThumbnailUrl(item), true),
+        requestPath: adapter.getItemRequestPath(item),
+        modifiedDate: adapter.getItemModifiedDate(item),
+        raw: item
+      })
+    })
+
+    data.nextPagePath = null
+
+    return data
   }
 }
 

+ 1 - 1
packages/@uppy/companion/src/server/provider/dropbox/adapter.js

@@ -36,5 +36,5 @@ exports.getItemModifiedDate = (item) => {
 }
 
 exports.getItemThumbnailUrl = (item) => {
-  return `${this.opts.serverUrl}/${this.Dropbox.id}/thumbnail/${this.getItemRequestPath(item)}`
+  return `/dropbox/thumbnail/${exports.getItemRequestPath(item)}`
 }

+ 26 - 3
packages/@uppy/companion/src/server/provider/dropbox/index.js

@@ -1,6 +1,7 @@
 const request = require('request')
 const purest = require('purest')({ request })
 const logger = require('../../logger')
+const adapter = require('./adapter')
 
 /**
  *
@@ -37,10 +38,14 @@ class DropBox {
     let stats
     let reqErr
     const finishReq = () => {
-      if (!reqErr) {
+      if (reqErr) {
+        done(reqErr)
+      } else if (stats.statusCode !== 200) {
+        done(new Error(`request to ${this.authProvider} returned ${stats.statusCode}`))
+      } else {
         stats.body.user_email = userInfo.body.email
+        done(null, this.adaptData(stats.body, options.uppy))
       }
-      done(reqErr, stats, stats.body)
     }
 
     this.stats(options, (err, resp) => {
@@ -129,8 +134,26 @@ class DropBox {
       })
   }
 
-  adaptData() {
+  adaptData (res, uppy) {
+    const data = { username: adapter.getUsername(res), items: [] }
+    const items = adapter.getItemSubList(res)
+    items.forEach((item) => {
+      data.items.push({
+        isFolder: adapter.isFolder(item),
+        icon: adapter.getItemIcon(item),
+        name: adapter.getItemName(item),
+        mimeType: adapter.getMimeType(item),
+        id: adapter.getItemId(item),
+        thumbnail: uppy.buildURL(adapter.getItemThumbnailUrl(item), true),
+        requestPath: adapter.getItemRequestPath(item),
+        modifiedDate: adapter.getItemModifiedDate(item),
+        raw: item
+      })
+    })
+
+    data.nextPagePath = null
 
+    return data
   }
 }
 

+ 3 - 4
packages/@uppy/companion/src/server/provider/instagram/adapter.js

@@ -65,10 +65,9 @@ exports.getItemModifiedDate = (item) => {
 }
 
 exports.getItemThumbnailUrl = (item) => {
-  return item.images.thumbnail.url
+  return item.images ? item.images.thumbnail.url : null
 }
 
-exports.getNextPagePath = (data) => {
-  const { files } = this.getPluginState()
-  return `recent?max_id=${this.getItemId(files[files.length - 1])}`
+exports.getNextPagePath = (items) => {
+  return `recent?max_id=${exports.getItemId(items[items.length - 1])}`
 }

+ 14 - 8
packages/@uppy/companion/src/server/provider/instagram/index.js

@@ -20,7 +20,15 @@ class Instagram {
       .select(`users/self/media/${directory}`)
       .qs(qs)
       .auth(token)
-      .request(done)
+      .request((err, resp, body) => {
+        if (err) {
+          done(err)
+        } else if (resp.statusCode !== 200) {
+          done(new Error(`request to ${this.authProvider} returned ${resp.statusCode}`))
+        } else {
+          done(null, this.adaptData(body))
+        }
+      })
   }
 
   _getMediaUrl (body, carouselId) {
@@ -88,12 +96,9 @@ class Instagram {
       })
   }
 
-  /** {username: string, items: [{
-   * items: [], icon: string, mimeType: string, id: string, isFolder: bool, name: string, requestPath: string, thumbnail: string, nextPagePath: string, modifiedDate: string
-   * }]} */
-  adaptData(res) {
+  adaptData (res) {
     const data = { username: adapter.getUsername(res), items: [] }
-    const items = adapter.getItemSubList(data)
+    const items = adapter.getItemSubList(res)
     items.forEach((item) => {
       data.items.push({
         isFolder: adapter.isFolder(item),
@@ -102,12 +107,13 @@ class Instagram {
         mimeType: adapter.getMimeType(item),
         id: adapter.getItemId(item),
         thumbnail: adapter.getItemThumbnailUrl(item),
-        nextPagePath: adapter.getNextPagePath(item),
         requestPath: adapter.getItemRequestPath(item),
-        modifiedDate: adapter.getItemModifiedDate(item)
+        modifiedDate: adapter.getItemModifiedDate(item),
+        raw: item
       })
     })
 
+    data.nextPagePath = adapter.getNextPagePath(items)
     return data
   }
 }

+ 9 - 2
packages/@uppy/companion/test/__mocks__/purest.js

@@ -14,17 +14,24 @@ class MockPurest {
       const responses = {
         dropbox: {
           hash: '0a9f95a989dd4b1851f0103c31e304ce',
+          user_email: 'foo@bar.com',
+          email: 'foo@bar.com',
           entries: [{ rev: 'f24234cd4' }]
         },
         drive: {
           kind: 'drive#fileList',
           etag: '"bcIyJ9A3gXa8oTYmz6nzAjQd-lY/eQc3WbZHkXpcItNyGKDuKXM_bNY"',
-          items: [{ id: '0B2x-PmqQHSKdT013TE1VVjZ3TWs' }],
+          files: [{
+            id: '0B2x-PmqQHSKdT013TE1VVjZ3TWs',
+            mimeType: 'image/jpg',
+            ownedByMe: true,
+            permissions: [{role: 'owner', emailAddress: 'ife@bala.com'}]
+          }],
           size: 300
         }
       }
       const body = responses[this.opts.providerName]
-      done(null, { body }, body)
+      done(null, { body, statusCode: 200 }, body)
     }
 
     return this

+ 2 - 2
packages/@uppy/companion/test/__tests__/companion.js

@@ -38,7 +38,7 @@ describe('list provider files', () => {
       .get('/dropbox/list/')
       .set('uppy-auth-token', token)
       .expect(200)
-      .then((res) => expect(res.body.hash).toBe('0a9f95a989dd4b1851f0103c31e304ce'))
+      .then((res) => expect(res.body.username).toBe('foo@bar.com'))
   })
 
   test('list files for google drive', () => {
@@ -46,7 +46,7 @@ describe('list provider files', () => {
       .get('/drive/list/')
       .set('uppy-auth-token', token)
       .expect(200)
-      .then((res) => expect(res.body.etag).toBe('"bcIyJ9A3gXa8oTYmz6nzAjQd-lY/eQc3WbZHkXpcItNyGKDuKXM_bNY"'))
+      .then((res) => expect(res.body.username).toBe('ife@bala.com'))
   })
 })
 

+ 0 - 45
packages/@uppy/dropbox/src/index.js

@@ -58,51 +58,6 @@ module.exports = class Dropbox extends Plugin {
     }
   }
 
-  getUsername (data) {
-    return data.user_email
-  }
-
-  isFolder (item) {
-    return item['.tag'] === 'folder'
-  }
-
-  getItemData (item) {
-    return item
-  }
-
-  getItemIcon (item) {
-    return item['.tag']
-  }
-
-  getItemSubList (item) {
-    return item.entries
-  }
-
-  getItemName (item) {
-    return item.name || ''
-  }
-
-  getMimeType (item) {
-    // mime types aren't supported.
-    return null
-  }
-
-  getItemId (item) {
-    return item.id
-  }
-
-  getItemRequestPath (item) {
-    return encodeURIComponent(item.path_lower)
-  }
-
-  getItemModifiedDate (item) {
-    return item.server_modified
-  }
-
-  getItemThumbnailUrl (item) {
-    return `${this.opts.serverUrl}/${this.Dropbox.id}/thumbnail/${this.getItemRequestPath(item)}`
-  }
-
   render (state) {
     return this.view.render(state)
   }

+ 1 - 1
packages/@uppy/google-drive/src/DriveProviderViews.js

@@ -6,7 +6,7 @@ module.exports = class DriveProviderViews extends ProviderViews {
     e.preventDefault()
 
     // Team Drives aren't selectable; for all else, defer to the base ProviderView.
-    if (file.kind !== 'drive#teamDrive') {
+    if (file.raw.kind !== 'drive#teamDrive') {
       super.toggleCheckbox(e, file)
     }
   }

+ 1 - 100
packages/@uppy/google-drive/src/index.js

@@ -61,110 +61,11 @@ module.exports = class GoogleDrive extends Plugin {
   onAuth (authenticated) {
     this.setPluginState({ authenticated })
     if (authenticated) {
-      this.view.getFolder('root')
-      this.getTeamDrives()
+      this.view.getFolder('root', '/')
     }
   }
 
-  getTeamDrives () {
-    this[this.id].get(`${this.GoogleDrive.id}/list/?listTeamDrives=true`)
-      .then((payload) => {
-        if (payload.teamDrives && payload.teamDrives.length) {
-          this.setPluginState({hasTeamDrives: true, teamDrives: payload.teamDrives})
-        }
-      })
-  }
-
-  getUsername (data) {
-    for (const item of data.files) {
-      if (item.ownedByMe) {
-        for (const permission of item.permissions) {
-          if (permission.role === 'owner') {
-            return permission.emailAddress
-          }
-        }
-      }
-    }
-  }
-
-  isFolder (item) {
-    return item.mimeType === 'application/vnd.google-apps.folder'
-  }
-
-  getItemData (item) {
-    return Object.assign({}, item, {size: parseFloat(item.size)})
-  }
-
-  getItemIcon (item) {
-    return item.iconLink
-  }
-
-  getItemSubList (item) {
-    return item.files.filter((i) => {
-      return this.isFolder(i) || !i.mimeType.startsWith('application/vnd.google')
-    })
-  }
-
-  getItemName (item) {
-    return item.name ? item.name : '/'
-  }
-
-  getMimeType (item) {
-    return item.mimeType
-  }
-
-  getItemId (item) {
-    return item.id
-  }
-
-  getItemRequestPath (item) {
-    // If it's from a Team Drive, add the Team Drive ID as a query param.
-    // The server needs the Team Drive ID to list files in a Team Drive folder.
-    if (item.teamDriveId) {
-      item.id += `?teamDriveId=${item.teamDriveId}`
-      delete item.teamDriveId
-    }
-
-    return this.getItemId(item)
-  }
-
-  getItemModifiedDate (item) {
-    return item.modifiedTime
-  }
-
-  getItemThumbnailUrl (item) {
-    return `${this.opts.serverUrl}/${this.GoogleDrive.id}/thumbnail/${this.getItemRequestPath(item)}`
-  }
-
   render (state) {
-    let pluginState = this.getPluginState()
-
-    // If the user has access to any Team Drives, handle them as needed.
-    if (pluginState.hasTeamDrives) {
-      let folders = pluginState.folders
-
-      // Remove any Team Drives we've previously pushed into the list of folders.
-      folders = folders.filter((folder) => {
-        return folder.kind !== 'drive#teamDrive'
-      })
-
-      // If viewing the Google Drive root, add Team Drives to the top of the list.
-      if (pluginState.directories.length === 1) {
-        pluginState.teamDrives.forEach((teamDrive) => {
-          folders.splice(0, 0, {
-            // Instead of a "normal" id, set it as a query param which will be handled by the server.
-            id: '?teamDriveId=' + teamDrive.id,
-            name: teamDrive.name,
-            kind: teamDrive.kind,
-            // Team Drives don't offer an icon, but do have a background image.
-            // The extra bit added onto the end crops/resizes the background image, yielding the same icon
-            // which is shown in the list of Team Drives within the Google Drive web UI.
-            iconLink: teamDrive.backgroundImageLink + '=w16-h16-n'
-          })
-        })
-      }
-      pluginState.folders = folders
-    }
     return this.view.render(state)
   }
 }

+ 0 - 79
packages/@uppy/instagram/src/index.js

@@ -64,85 +64,6 @@ module.exports = class Instagram extends Plugin {
     }
   }
 
-  getUsername (data) {
-    return data.data[0].user.username
-  }
-
-  isFolder (item) {
-    return false
-  }
-
-  getItemData (item) {
-    return item
-  }
-
-  getItemIcon (item) {
-    if (!item.images) {
-      return 'video'
-    }
-    return item.images.low_resolution.url
-  }
-
-  getItemSubList (item) {
-    const subItems = []
-    item.data.forEach((subItem) => {
-      if (subItem.carousel_media) {
-        subItem.carousel_media.forEach((i, index) => {
-          const { id, created_time } = subItem
-          const newSubItem = Object.assign({}, i, { id, created_time })
-          newSubItem.carousel_id = index
-          subItems.push(newSubItem)
-        })
-      } else {
-        subItems.push(subItem)
-      }
-    })
-    return subItems
-  }
-
-  getItemName (item) {
-    if (item && item['created_time']) {
-      const ext = item.type === 'video' ? 'mp4' : 'jpeg'
-      let date = new Date(item['created_time'] * 1000)
-      date = date.toLocaleDateString([], {
-        year: 'numeric',
-        month: 'short',
-        day: 'numeric',
-        hour: 'numeric',
-        minute: 'numeric'
-      })
-      // adding both date and carousel_id, so the name is unique
-      return `Instagram ${date}${item.carousel_id ? ' ' + item.carousel_id : ''}.${ext}`
-    }
-    return ''
-  }
-
-  getMimeType (item) {
-    return item.type === 'video' ? 'video/mp4' : 'image/jpeg'
-  }
-
-  getItemId (item) {
-    return `${item.id}${item.carousel_id || ''}`
-  }
-
-  getItemRequestPath (item) {
-    const suffix = isNaN(item.carousel_id) ? '' : `?carousel_id=${item.carousel_id}`
-    return `${item.id}${suffix}`
-  }
-
-  getItemModifiedDate (item) {
-    return item.created_time
-  }
-
-  getItemThumbnailUrl (item) {
-    return item.images.thumbnail.url
-  }
-
-  getNextPagePath () {
-    const { files } = this.getPluginState()
-    return `recent?max_id=${this.getItemId(files[files.length - 1])}`
-  }
-
   render (state) {
     return this.view.render(state)
   }

+ 0 - 3
packages/@uppy/provider-views/src/Browser.js

@@ -48,12 +48,9 @@ const Browser = (props) => {
         isChecked={props.isChecked}
         handleFolderClick={props.getNextFolder}
         toggleCheckbox={props.toggleCheckbox}
-        getItemName={props.getItemName}
-        getItemIcon={props.getItemIcon}
         handleScroll={props.handleScroll}
         title={props.title}
         showTitles={props.showTitles}
-        getItemId={props.getItemId}
         i18n={props.i18n}
       />
       {selected > 0 && <FooterActions selected={selected} {...props} />}

+ 6 - 6
packages/@uppy/provider-views/src/ItemList.js

@@ -19,11 +19,11 @@ module.exports = (props) => {
             isDisabled = isChecked.loading
           }
           return Row({
-            title: props.getItemName(folder),
-            id: props.getItemId(folder),
+            title: folder.name,
+            id: folder.id,
             type: 'folder',
             // active: props.activeRow(folder),
-            getItemIcon: () => props.getItemIcon(folder),
+            getItemIcon: () => folder.icon,
             isDisabled: isDisabled,
             isChecked: isChecked,
             handleFolderClick: () => props.handleFolderClick(folder),
@@ -34,11 +34,11 @@ module.exports = (props) => {
         })}
         {props.files.map(file => {
           return Row({
-            title: props.getItemName(file),
-            id: props.getItemId(file),
+            title: file.name,
+            id: file.id,
             type: 'file',
             // active: props.activeRow(file),
-            getItemIcon: () => props.getItemIcon(file),
+            getItemIcon: () => file.icon,
             isDisabled: false,
             isChecked: props.isChecked(file),
             handleClick: (e) => props.toggleCheckbox(e, file),

+ 34 - 64
packages/@uppy/provider-views/src/index.js

@@ -27,34 +27,7 @@ class CloseWrapper extends Component {
 }
 
 /**
- * Class to easily generate generic views for plugins
- *
- *
- * This class expects the plugin instance using it to have the following
- * accessor methods.
- * Each method takes the item whose property is to be accessed
- * as a param
- *
- * isFolder
- *    @return {Boolean} for if the item is a folder or not
- * getItemData
- *    @return {Object} that is format ready for uppy upload/download
- * getItemIcon
- *    @return {Object} html instance of the item's icon
- * getItemSubList
- *    @return {Array} sub-items in the item. e.g a folder may contain sub-items
- * getItemName
- *    @return {String} display friendly name of the item
- * getMimeType
- *    @return {String} mime type of the item
- * getItemId
- *    @return {String} unique id of the item
- * getItemRequestPath
- *    @return {String} unique request path of the item when making calls to Companion
- * getItemModifiedDate
- *    @return {object} or {String} date of when last the item was modified
- * getItemThumbnailUrl
- *    @return {String}
+ * Class to easily generate generic views for Provider plugins
  */
 module.exports = class ProviderView {
   /**
@@ -108,8 +81,8 @@ module.exports = class ProviderView {
   }
 
   _updateFilesAndFolders (res, files, folders) {
-    this.plugin.getItemSubList(res).forEach((item) => {
-      if (this.plugin.isFolder(item)) {
+    res.items.forEach((item) => {
+      if (item.isFolder) {
         folders.push(item)
       } else {
         files.push(item)
@@ -151,10 +124,11 @@ module.exports = class ProviderView {
         if (index !== -1) {
           updatedDirectories = state.directories.slice(0, index + 1)
         } else {
-          updatedDirectories = state.directories.concat([{id, title: name || this.plugin.getItemName(res)}])
+          updatedDirectories = state.directories.concat([{id, title: name}])
         }
 
-        this.username = this.username ? this.username : this.plugin.getUsername(res)
+        this.username = this.username ? this.username : res.username
+        this.nextPagePath = res.nextPagePath
         this._updateFilesAndFolders(res, files, folders)
         this.plugin.setPluginState({ directories: updatedDirectories })
       },
@@ -167,8 +141,7 @@ module.exports = class ProviderView {
    * @param  {String} title Folder title
    */
   getNextFolder (folder) {
-    let id = this.plugin.getItemRequestPath(folder)
-    this.getFolder(id, this.plugin.getItemName(folder))
+    this.getFolder(folder.requestPath, folder.name)
     this.lastCheckbox = undefined
   }
 
@@ -176,18 +149,18 @@ module.exports = class ProviderView {
     const tagFile = {
       id: this.providerFileToId(file),
       source: this.plugin.id,
-      data: this.plugin.getItemData(file),
-      name: this.plugin.getItemName(file) || this.plugin.getItemId(file),
-      type: this.plugin.getMimeType(file),
+      data: file,
+      name: file.name || file.id,
+      type: file.mimeType,
       isRemote: true,
       body: {
-        fileId: this.plugin.getItemId(file)
+        fileId: file.id
       },
       remote: {
         serverUrl: this.plugin.opts.serverUrl,
-        url: `${this.Provider.fileUrl(this.plugin.getItemRequestPath(file))}`,
+        url: `${this.Provider.fileUrl(file.requestPath)}`,
         body: {
-          fileId: this.plugin.getItemId(file)
+          fileId: file.id
         },
         providerOptions: this.Provider.opts
       }
@@ -196,7 +169,7 @@ module.exports = class ProviderView {
     const fileType = getFileType(tagFile)
     // TODO Should we just always use the thumbnail URL if it exists?
     if (fileType && isPreviewSupported(fileType)) {
-      tagFile.preview = this.plugin.getItemThumbnailUrl(file)
+      tagFile.preview = file.thumbnail
     }
     this.plugin.uppy.log('Adding remote file')
     try {
@@ -253,7 +226,7 @@ module.exports = class ProviderView {
       return items
     }
     return items.filter((folder) => {
-      return this.plugin.getItemName(folder).toLowerCase().indexOf(state.filterInput.toLowerCase()) !== -1
+      return folder.name.toLowerCase().indexOf(state.filterInput.toLowerCase()) !== -1
     })
   }
 
@@ -263,16 +236,16 @@ module.exports = class ProviderView {
 
     let sortedFiles = files.sort((fileA, fileB) => {
       if (sorting === 'titleDescending') {
-        return this.plugin.getItemName(fileB).localeCompare(this.plugin.getItemName(fileA))
+        return fileB.name.localeCompare(fileA.name)
       }
-      return this.plugin.getItemName(fileA).localeCompare(this.plugin.getItemName(fileB))
+      return fileA.name.localeCompare(fileB.name)
     })
 
     let sortedFolders = folders.sort((folderA, folderB) => {
       if (sorting === 'titleDescending') {
-        return this.plugin.getItemName(folderB).localeCompare(this.plugin.getItemName(folderA))
+        return folderB.name.localeCompare(folderA.name)
       }
-      return this.plugin.getItemName(folderA).localeCompare(this.plugin.getItemName(folderB))
+      return folderA.name.localeCompare(folderB.name)
     })
 
     this.plugin.setPluginState(Object.assign({}, state, {
@@ -287,8 +260,8 @@ module.exports = class ProviderView {
     const {files, folders, sorting} = state
 
     let sortedFiles = files.sort((fileA, fileB) => {
-      let a = new Date(this.plugin.getItemModifiedDate(fileA))
-      let b = new Date(this.plugin.getItemModifiedDate(fileB))
+      let a = new Date(fileA.modifiedDate)
+      let b = new Date(fileB.modifiedDate)
 
       if (sorting === 'dateDescending') {
         return a > b ? -1 : a < b ? 1 : 0
@@ -297,8 +270,8 @@ module.exports = class ProviderView {
     })
 
     let sortedFolders = folders.sort((folderA, folderB) => {
-      let a = new Date(this.plugin.getItemModifiedDate(folderA))
-      let b = new Date(this.plugin.getItemModifiedDate(folderB))
+      let a = new Date(folderA.modifiedDate)
+      let b = new Date(folderB.modifiedDate)
 
       if (sorting === 'dateDescending') {
         return a > b ? -1 : a < b ? 1 : 0
@@ -324,8 +297,8 @@ module.exports = class ProviderView {
     }
 
     let sortedFiles = files.sort((fileA, fileB) => {
-      let a = this.plugin.getItemData(fileA).size
-      let b = this.plugin.getItemData(fileB).size
+      let a = fileA.size
+      let b = fileB.size
 
       if (sorting === 'sizeDescending') {
         return a > b ? -1 : a < b ? 1 : 0
@@ -363,10 +336,10 @@ module.exports = class ProviderView {
     }
     folders[folderId] = {loading: true, files: []}
     this.plugin.setPluginState({selectedFolders: folders})
-    return this.Provider.list(this.plugin.getItemRequestPath(folder)).then((res) => {
+    return this.Provider.list(folder.requestPath).then((res) => {
       let files = []
-      this.plugin.getItemSubList(res).forEach((item) => {
-        if (!this.plugin.isFolder(item)) {
+      res.items.forEach((item) => {
+        if (!item.isFolder) {
           this.addFile(item)
           files.push(this.providerFileToId(item))
         }
@@ -378,7 +351,7 @@ module.exports = class ProviderView {
       let message
       if (files.length) {
         message = dashboard.i18n('folderAdded', {
-          smart_count: files.length, folder: this.plugin.getItemName(folder)
+          smart_count: files.length, folder: folder.name
         })
       } else {
         message = dashboard.i18n('emptyFolderAdded')
@@ -435,9 +408,9 @@ module.exports = class ProviderView {
 
   providerFileToId (file) {
     return generateFileID({
-      data: this.plugin.getItemData(file),
-      name: this.plugin.getItemName(file) || this.plugin.getItemId(file),
-      type: this.plugin.getMimeType(file)
+      data: file,
+      name: file.name || file.id,
+      type: file.mimeType
     })
   }
 
@@ -490,7 +463,7 @@ module.exports = class ProviderView {
 
   handleScroll (e) {
     const scrollPos = e.target.scrollHeight - (e.target.scrollTop + e.target.offsetHeight)
-    const path = this.plugin.getNextPagePath ? this.plugin.getNextPagePath() : null
+    const path = this.nextPagePath ? this.nextPagePath : null
 
     if (scrollPos < 50 && path && !this._isHandlingScroll) {
       this.Provider.list(path)
@@ -507,7 +480,7 @@ module.exports = class ProviderView {
   donePicking () {
     const { currentSelection } = this.plugin.getPluginState()
     const promises = currentSelection.map((file) => {
-      if (this.plugin.isFolder(file)) {
+      if (file.isFolder) {
         return this.addFolder(file)
       } else {
         return this.addFile(file)
@@ -586,9 +559,6 @@ module.exports = class ProviderView {
       isActiveRow: this.isActiveRow,
       isChecked: this.isChecked,
       toggleCheckbox: this.toggleCheckbox,
-      getItemId: this.plugin.getItemId,
-      getItemName: this.plugin.getItemName,
-      getItemIcon: this.plugin.getItemIcon,
       handleScroll: this.handleScroll,
       done: this.donePicking,
       cancel: this.cancelPicking,