Просмотр исходного кода

companion,onedrive: implement OneDrive Provider plugin

ifedapoolarewaju 5 лет назад
Родитель
Сommit
f1d6f6b6dc

+ 2 - 0
examples/dev/Dashboard.js

@@ -2,6 +2,7 @@ const Uppy = require('@uppy/core/src')
 const Dashboard = require('@uppy/dashboard/src')
 const Instagram = require('@uppy/instagram/src')
 const Facebook = require('@uppy/facebook/src')
+const OneDrive = require('@uppy/onedrive/src')
 const Dropbox = require('@uppy/dropbox/src')
 const GoogleDrive = require('@uppy/google-drive/src')
 const Url = require('@uppy/url/src')
@@ -37,6 +38,7 @@ module.exports = () => {
     .use(Instagram, { target: Dashboard, companionUrl: 'http://localhost:3020' })
     .use(Dropbox, { target: Dashboard, companionUrl: 'http://localhost:3020' })
     .use(Facebook, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+    .use(OneDrive, { target: Dashboard, companionUrl: 'http://localhost:3020' })
     .use(Url, { target: Dashboard, companionUrl: 'http://localhost:3020' })
     .use(Webcam, { target: Dashboard })
     .use(Tus, { endpoint: TUS_ENDPOINT })

+ 5 - 0
packages/@uppy/companion/src/config/grant.js

@@ -22,6 +22,11 @@ module.exports = () => {
       transport: 'session',
       scope: ['email', 'user_photos'],
       callback: '/facebook/callback'
+    },
+    microsoft: {
+      transport: 'session',
+      scope: ['files.read.all', 'offline_access', 'User.Read'],
+      callback: '/onedrive/callback'
     }
   }
 }

+ 2 - 1
packages/@uppy/companion/src/server/provider/index.js

@@ -7,6 +7,7 @@ const dropbox = require('./dropbox')
 const drive = require('./drive')
 const instagram = require('./instagram')
 const facebook = require('./facebook')
+const onedrive = require('./onedrive')
 const { getURLBuilder } = require('../helpers/utils')
 const logger = require('../logger')
 
@@ -92,7 +93,7 @@ module.exports.getProviderMiddleware = (providers) => {
  * @return {Object.<string, typeof Provider>}
  */
 module.exports.getDefaultProviders = () => {
-  return { dropbox, drive, instagram, facebook }
+  return { dropbox, drive, instagram, facebook, onedrive }
 }
 
 /**

+ 50 - 0
packages/@uppy/companion/src/server/provider/onedrive/adapter.js

@@ -0,0 +1,50 @@
+const querystring = require('querystring')
+
+exports.isFolder = (item) => {
+  return !!item.folder
+}
+
+exports.getItemSize = (item) => {
+  return item.size
+}
+
+exports.getItemIcon = (item) => {
+  return exports.isFolder(item) ? 'folder' : exports.getItemThumbnailUrl(item)
+}
+
+exports.getItemSubList = (item) => {
+  return item.value
+}
+
+exports.getItemName = (item) => {
+  return item.name || ''
+}
+
+exports.getMimeType = (item) => {
+  return item.file ? item.file.mimeType : null
+}
+
+exports.getItemId = (item) => {
+  return item.id
+}
+
+exports.getItemRequestPath = (item) => {
+  return exports.getItemId(item)
+}
+
+exports.getItemModifiedDate = (item) => {
+  return item.fileSystemInfo.lastModifiedDateTime
+}
+
+exports.getItemThumbnailUrl = (item) => {
+  return item.thumbnails[0] ? item.thumbnails[0].medium.url : null
+}
+
+exports.getNextPagePath = (data) => {
+  if (!data['@odata.nextLink']) {
+    return null
+  }
+
+  const query = { cursor: querystring.parse(data['@odata.nextLink']).$skiptoken }
+  return `?${querystring.stringify(query)}`
+}

+ 126 - 0
packages/@uppy/companion/src/server/provider/onedrive/index.js

@@ -0,0 +1,126 @@
+const request = require('request')
+const purest = require('purest')({ request })
+const logger = require('../../logger')
+const adapter = require('./adapter')
+const AuthError = require('../error')
+
+class OneDrive {
+  constructor (options) {
+    this.authProvider = options.provider = OneDrive.authProvider
+    this.client = purest(options)
+  }
+
+  static get authProvider () {
+    return 'microsoft'
+  }
+
+  _userInfo ({ token }, done) {
+    this.client
+      .get('me')
+      .auth(token)
+      .request(done)
+  }
+
+  /**
+   * Makes 2 requests in parallel - 1. to get files, 2. to get user email
+   * it then waits till both requests are done before proceeding with the callback
+   *
+   * @param {object} options
+   * @param {function} done
+   */
+  list ({ directory, query, token }, done) {
+    const path = directory ? `items/${directory}` : 'root'
+    const qs = { $expand: 'thumbnails' }
+    if (query.cursor) {
+      qs.$skiptoken = query.cursor
+    }
+
+    this.client
+      .get(`/drive/${path}/children`)
+      .qs(qs)
+      .auth(token)
+      .request((err, resp, body) => {
+        if (err || resp.statusCode !== 200) {
+          err = this._error(err, resp)
+          logger.error(err, 'provider.onedrive.list.error')
+          return done(err)
+        } else {
+          this._userInfo({ token }, (err, infoResp) => {
+            if (err || infoResp.statusCode !== 200) {
+              err = this._error(err, infoResp)
+              logger.error(err, 'provider.onedrive.user.error')
+              return done(err)
+            }
+            done(null, this.adaptData(body, infoResp.body.mail || infoResp.body.userPrincipalName))
+          })
+        }
+      })
+  }
+
+  download ({ id, token }, onData) {
+    return this.client
+      .get(`/drive/items/${id}/content`)
+      .auth(token)
+      .request()
+      .on('data', onData)
+      .on('end', () => onData(null))
+      .on('error', (err) => {
+        logger.error(err, 'provider.onedrive.download.error')
+      })
+  }
+
+  thumbnail (_, done) {
+    // not implementing this because a public thumbnail from onedrive will be used instead
+    const err = new Error('call to thumbnail is not implemented')
+    logger.error(err, 'provider.onedrive.thumbnail.error')
+    return done(err)
+  }
+
+  size ({ id, token }, done) {
+    return this.client
+      .get(`/drive/items/${id}`)
+      .auth(token)
+      .request((err, resp, body) => {
+        if (err || resp.statusCode !== 200) {
+          err = this._error(err, resp)
+          logger.error(err, 'provider.onedrive.size.error')
+          return done(err)
+        } else {
+          done(null, body.size)
+        }
+      })
+  }
+
+  adaptData (res, username) {
+    const data = { username, 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: adapter.getItemThumbnailUrl(item),
+        requestPath: adapter.getItemRequestPath(item),
+        modifiedDate: adapter.getItemModifiedDate(item),
+        size: adapter.getItemSize(item)
+      })
+    })
+
+    data.nextPagePath = adapter.getNextPagePath(res)
+
+    return data
+  }
+
+  _error (err, resp) {
+    if (resp) {
+      const errMsg = `request to ${this.authProvider} returned ${resp.statusCode}`
+      return resp.statusCode === 401 ? new AuthError() : new Error(errMsg)
+    }
+
+    return err
+  }
+}
+
+module.exports = OneDrive

+ 4 - 0
packages/@uppy/companion/src/standalone/helper.js

@@ -45,6 +45,10 @@ const getConfigFromEnv = () => {
         key: process.env.COMPANION_FACEBOOK_KEY,
         secret: getSecret('COMPANION_FACEBOOK_SECRET')
       },
+      microsoft: {
+        key: process.env.COMPANION_ONEDRIVE_KEY,
+        secret: getSecret('COMPANION_ONEDRIVE_SECRET')
+      },
       s3: {
         key: process.env.COMPANION_AWS_KEY,
         secret: getSecret('COMPANION_AWS_SECRET'),

+ 21 - 0
packages/@uppy/onedrive/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2019 Transloadit
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 40 - 0
packages/@uppy/onedrive/README.md

@@ -0,0 +1,40 @@
+# @uppy/facebook
+
+<img src="https://uppy.io/images/logos/uppy-dog-head-arrow.svg" width="120" alt="Uppy logo: a superman puppy in a pink suit" align="right">
+
+<a href="https://www.npmjs.com/package/@uppy/facebook"><img src="https://img.shields.io/npm/v/@uppy/facebook.svg?style=flat-square"></a>
+<a href="https://travis-ci.org/transloadit/uppy"><img src="https://img.shields.io/travis/transloadit/uppy/master.svg?style=flat-square" alt="Build Status"></a>
+
+A description of this plugin or module goes here.
+
+Uppy is being developed by the folks at [Transloadit](https://transloadit.com), a versatile file encoding service.
+
+## Example
+
+```js
+const Uppy = require('@uppy/core')
+const Facebook = require('@uppy/facebook')
+
+const uppy = Uppy()
+uppy.use(Facebook, {
+  // Options
+})
+```
+
+## Installation
+
+```bash
+$ npm install @uppy/facebook --save
+```
+
+We recommend installing from npm and then using a module bundler such as [Webpack](https://webpack.js.org/), [Browserify](http://browserify.org/) or [Rollup.js](http://rollupjs.org/).
+
+Alternatively, you can also use this plugin in a pre-built bundle from Transloadit's CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object. See the [main Uppy documentation](https://uppy.io/docs/#Installation) for instructions.
+
+## Documentation
+
+Documentation for this plugin can be found on the [Uppy website](https://uppy.io/docs/facebook).
+
+## License
+
+[The MIT License](./LICENSE).

+ 31 - 0
packages/@uppy/onedrive/package.json

@@ -0,0 +1,31 @@
+{
+  "name": "@uppy/onedrive",
+  "description": "Import files from Facbook, into Uppy.",
+  "version": "1.2.0",
+  "license": "MIT",
+  "main": "lib/index.js",
+  "types": "types/index.d.ts",
+  "keywords": [
+    "file uploader",
+    "uppy",
+    "uppy-plugin",
+    "onedrive"
+  ],
+  "homepage": "https://uppy.io",
+  "bugs": {
+    "url": "https://github.com/transloadit/uppy/issues"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/transloadit/uppy.git"
+  },
+  "dependencies": {
+    "@uppy/companion-client": "file:../companion-client",
+    "@uppy/provider-views": "file:../provider-views",
+    "@uppy/utils": "file:../utils",
+    "preact": "8.2.9"
+  },
+  "peerDependencies": {
+    "@uppy/core": "^1.0.0"
+  }
+}

+ 66 - 0
packages/@uppy/onedrive/src/index.js

@@ -0,0 +1,66 @@
+const { Plugin } = require('@uppy/core')
+const { Provider } = require('@uppy/companion-client')
+const ProviderViews = require('@uppy/provider-views')
+const { h } = require('preact')
+
+module.exports = class OneDrive extends Plugin {
+  static VERSION = require('../package.json').version
+
+  constructor (uppy, opts) {
+    super(uppy, opts)
+    this.id = this.opts.id || 'OneDrive'
+    Provider.initPlugin(this, opts)
+    this.title = this.opts.title || 'OneDrive'
+    this.icon = () => (
+      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
+        <path fill="#1565c0" d="M40.429,35.999c0,0,2.89-0.393,3.47-3.185C43.964,32.502,44,32.161,44,31.787 c0-0.233-0.015-0.454-0.044-0.665c-0.428-3.158-3.852-3.868-3.852-3.868s0.595-3.401-2.543-5.183c-3.138-1.78-6.005,0-6.005,0 s-1.678-3.401-6.222-3.401c-5.843,0-6.817,6.64-6.817,6.64S13,25.636,13,30.493C13,35.352,18.031,36,18.031,36L40.429,35.999 L40.429,35.999z" />
+        <path fill="#1565c0" d="M11,30.493c0-4.395,3.286-6.319,5.875-6.945c0.898-2.954,3.384-6.878,8.46-6.878 c0.006,0,0.011,0.001,0.017,0.001c0.007,0,0.013-0.001,0.02-0.001c3.522,0,5.71,1.646,6.892,2.953 c0.65-0.191,1.448-0.343,2.347-0.343c0.004,0,0.007,0.001,0.011,0.001c0.003,0,0.006,0,0.01,0c0.02,0,0.039,0.004,0.059,0.004 C34.729,19,34.063,12,26.013,12c-5.503,0-7.446,4.691-7.446,4.691s-3.992-2.965-8.092,1.133c-2.105,2.104-1.619,5.338-1.619,5.338 S4,23.648,4,28.825C4.001,33.515,9.018,34,9.018,34h2.807C11.32,33.041,11,31.886,11,30.493z" />
+      </svg>
+    )
+
+    this.provider = new Provider(uppy, {
+      companionUrl: this.opts.companionUrl,
+      serverHeaders: this.opts.serverHeaders,
+      storage: this.opts.storage,
+      provider: 'onedrive',
+      pluginId: this.id
+    })
+
+    this.onFirstRender = this.onFirstRender.bind(this)
+    this.render = this.render.bind(this)
+  }
+
+  install () {
+    this.view = new ProviderViews(this, {
+      provider: this.provider
+    })
+    // Set default state for Dropbox
+    this.setPluginState({
+      authenticated: false,
+      files: [],
+      folders: [],
+      directories: [],
+      activeRow: -1,
+      filterInput: '',
+      isSearchVisible: false
+    })
+
+    const target = this.opts.target
+    if (target) {
+      this.mount(target, this)
+    }
+  }
+
+  uninstall () {
+    this.view.tearDown()
+    this.unmount()
+  }
+
+  onFirstRender () {
+    return this.view.getFolder()
+  }
+
+  render (state) {
+    return this.view.render(state)
+  }
+}

+ 21 - 0
packages/@uppy/onedrive/types/index.d.ts

@@ -0,0 +1,21 @@
+import Uppy = require('@uppy/core');
+import CompanionClient = require('@uppy/companion-client');
+
+declare module OneDrive {
+  interface OneDriveOptions extends Uppy.PluginOptions, CompanionClient.ProviderOptions {
+    companionUrl: string;
+    companionAllowedHosts: string | RegExp | Array<string | RegExp>;
+  }
+}
+
+declare class OneDrive extends Uppy.Plugin {
+  constructor(uppy: Uppy.Uppy, opts: Partial<OneDrive.OneDriveOptions>);
+}
+
+export = OneDrive;
+
+declare module '@uppy/core' {
+  export interface Uppy {
+    use(pluginClass: typeof OneDrive, opts: Partial<OneDrive.OneDriveOptions>): Uppy.Uppy;
+  }
+}