Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Find and fetch #183

Merged
merged 2 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions lib/api.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { EventEmitter } from 'events'
import { Agent as HttpAgent } from 'http'
import { Agent as HttpsAgent } from 'https'
import fetch from 'node-fetch'
import AbortController from './abort-controller-polyfill.mjs'
import { createPromise } from './util.mjs'

Expand Down Expand Up @@ -63,7 +62,7 @@ class API extends EventEmitter {
this.closed = false
}

defaultFetch (url, opts) {
async defaultFetch (url, opts) {
if (!opts) opts = {}
if (!opts.agent) {
opts.agent = url => url.protocol === 'http:' ? this.httpAgent : this.httpsAgent
Expand All @@ -74,7 +73,16 @@ class API extends EventEmitter {
if (!opts.headers['user-agent']) opts.headers['user-agent'] = this.userAgent
}

return fetch(url, opts)
if (!API.fetchModule) {
if (typeof globalThis.fetch === 'function') {
API.fetchModule = globalThis.fetch
} else {
const nodeFetch = await import('node-fetch')
API.fetchModule = nodeFetch.fetch
}
}

return API.fetchModule(url, opts)
}

request (json, originalCb, retryno = 0) {
Expand Down
60 changes: 60 additions & 0 deletions lib/file.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,66 @@ class File extends EventEmitter {
return promise
}

find (query, deep) {
if (!this.children) throw Error('You can only call .find on directories')

if (typeof query === 'string') {
const queryString = query
query = file => file.name === queryString
} else if (Array.isArray(query)) {
const queryArray = query
query = file => queryArray.includes(file.name)
}
if (typeof query !== 'function') {
throw Error('Query must be a file matching function, an array of valid file names or a string with a file name')
}

return this.children.reduce((result, entry) => {
if (result) return result
if (entry.children) {
return deep ? entry.find(query, deep) : null
}
return query(entry) ? entry : null
}, null)
}

filter (query, deep) {
if (!this.children) throw Error('You can only call .filter on directories')

if (typeof query === 'string') {
const queryString = query
query = file => file.name === queryString
} else if (Array.isArray(query)) {
const queryArray = query
query = file => queryArray.includes(file.name)
}
if (typeof query !== 'function') {
throw Error('Query must be a file matching function, an array of valid file names or a string with a file name')
}

return this.children.reduce((results, entry) => {
if (entry.children) {
if (deep) return results.concat(entry.find(query, deep))
return results
}
return query(entry) ? results.concat(entry) : results
}, [])
}

navigate (query, deep) {
if (!this.children) throw Error('You can only call .navigate on directories')

if (typeof query === 'string') {
query = query.split('/')
} else if (!Array.isArray(query)) {
throw Error('Query must be an array or a string')
}

return query.reduce((node, name) => {
return node && node.children && node.children.find(e => e.name === name)
}, this)
}

static fromURL (opt, extraOpt = {}) {
if (typeof opt === 'object') {
// todo: warn to use File directly
Expand Down
24 changes: 24 additions & 0 deletions lib/storage.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,30 @@ class Storage extends EventEmitter {
return this.root.upload(opt, buffer, cb)
}

// alternative to this.root.find
find (query, deep) {
if (this.status !== 'ready') {
throw Error('storage is not ready')
}
return this.root.find(query, deep)
}

// alternative to this.root.filter
filter (query, deep) {
if (this.status !== 'ready') {
throw Error('storage is not ready')
}
return this.root.filter(query, deep)
}

// alternative to this.root.navigate
navigate (query, deep) {
if (this.status !== 'ready') {
throw Error('storage is not ready')
}
return this.root.navigate(query, deep)
}

close (cb) {
// Does not handle still connecting or incomplete streams
this.status = 'closed'
Expand Down
40 changes: 40 additions & 0 deletions test/storage.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,46 @@ test.serial('Should stream as upload arguments', async t => {
t.is(file.size, dataSize)
})

test.serial('Should find files using functions', t => {
const matchingFile = storage.find(e => e.name.includes('test file streams'))
t.is(matchingFile.size, 2097152)
})

test.serial('Should find files using string', t => {
const matchingFile = storage.find('file in folder 2', true)
t.is(matchingFile.size, 16)
})

test.serial('Should find files using arrays', t => {
const matchingFile = storage.find(['file in folder 2'], true)
t.is(matchingFile.size, 16)
})

test.serial('Should filter files using functions', t => {
const matchingFiles = storage.filter(e => e.name.includes('test file streams'))
t.is(matchingFiles.length, 4)
})

test.serial('Should filter files using string', t => {
const matchingFiles = storage.filter('file in folder 2', true)
t.is(matchingFiles.length, 1)
})

test.serial('Should filter files using arrays', t => {
const matchingFiles = storage.filter(['test file streams', 'test file streams 2', 'file in folder 2'], true)
t.is(matchingFiles.length, 5)
})

test.serial('Should navigate to files using arrays', t => {
const matchingFile = storage.navigate(['test folder', 'test folder 2', 'file in folder 2'])
t.is(matchingFile.size, 16)
})

test.serial('Should navigate to files using strings', t => {
const matchingFile = storage.navigate('test folder/test folder 2/file in folder 2')
t.is(matchingFile.size, 16)
})

test.serial('Should logout from MEGA', t => {
return new Promise((resolve, reject) => {
storage.close((error) => {
Expand Down
Loading