123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- const got = require('got').default
- const Provider = require('../Provider')
- const adaptData = require('./adapter')
- const { withProviderErrorHandling } = require('../providerErrors')
- const { prepareStream } = require('../../helpers/utils')
- const BOX_FILES_FIELDS = 'id,modified_at,name,permissions,size,type'
- const BOX_THUMBNAIL_SIZE = 256
- const getClient = ({ token }) => got.extend({
- prefixUrl: 'https://api.box.com/2.0',
- headers: {
- authorization: `Bearer ${token}`,
- },
- })
- async function getUserInfo ({ token }) {
- return getClient({ token }).get('users/me', { responseType: 'json' }).json()
- }
- async function list ({ directory, query, token }) {
- const rootFolderID = '0'
- // https://developer.box.com/reference/resources/items/
- return getClient({ token }).get(`folders/${directory || rootFolderID}/items`, { searchParams: { fields: BOX_FILES_FIELDS, offset: query.cursor, limit: 1000 }, responseType: 'json' }).json()
- }
- /**
- * Adapter for API https://developer.box.com/reference/
- */
- class Box extends Provider {
- constructor (options) {
- super(options)
- // needed for the thumbnails fetched via companion
- this.needsCookieAuth = true
- }
- static get authProvider () {
- return 'box'
- }
- /**
- * Lists files and folders from Box API
- *
- * @param {object} options
- * @param {string} options.directory
- * @param {any} options.query
- * @param {string} options.token
- * @param {unknown} options.companion
- */
- async list ({ directory, token, query, companion }) {
- return this.#withErrorHandling('provider.box.list.error', async () => {
- const [userInfo, files] = await Promise.all([
- getUserInfo({ token }),
- list({ directory, query, token }),
- ])
- return adaptData(files, userInfo.login, companion)
- })
- }
- async download ({ id, token }) {
- return this.#withErrorHandling('provider.box.download.error', async () => {
- const stream = getClient({ token }).stream.get(`files/${id}/content`, { responseType: 'json' })
- await prepareStream(stream)
- return { stream }
- })
- }
- async thumbnail ({ id, token }) {
- return this.#withErrorHandling('provider.box.thumbnail.error', async () => {
- const extension = 'jpg' // you can set this to png to more easily reproduce http 202 retry-after
- // From box API docs:
- // Sometimes generating a thumbnail can take a few seconds.
- // In these situations the API returns a Location-header pointing to a placeholder graphic
- // for this file type.
- // The placeholder graphic can be used in a user interface until the thumbnail generation has completed.
- // The Retry-After-header indicates when to the thumbnail will be ready.
- // At that time, retry this endpoint to retrieve the thumbnail.
- //
- // This can be reproduced more easily by changing extension to png and trying on a newly uploaded image
- const stream = getClient({ token }).stream.get(`files/${id}/thumbnail.${extension}`, {
- searchParams: { max_height: BOX_THUMBNAIL_SIZE, max_width: BOX_THUMBNAIL_SIZE },
- responseType: 'json',
- })
- await prepareStream(stream)
- return { stream, contentType: 'image/jpeg' }
- })
- }
- async size ({ id, token }) {
- return this.#withErrorHandling('provider.box.size.error', async () => {
- const { size } = await getClient({ token }).get(`files/${id}`, { responseType: 'json' }).json()
- return parseInt(size, 10)
- })
- }
- logout ({ companion, token }) {
- return this.#withErrorHandling('provider.box.logout.error', async () => {
- const { key, secret } = companion.options.providerOptions.box
- await getClient({ token }).post('oauth2/revoke', {
- prefixUrl: 'https://api.box.com',
- form: {
- client_id: key,
- client_secret: secret,
- token,
- },
- responseType: 'json',
- })
- return { revoked: true }
- })
- }
- // eslint-disable-next-line class-methods-use-this
- async #withErrorHandling (tag, fn) {
- return withProviderErrorHandling({
- fn,
- tag,
- providerName: Box.authProvider,
- isAuthError: (response) => response.statusCode === 401,
- getJsonErrorMessage: (body) => body?.message,
- })
- }
- }
- module.exports = Box
|