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

feat(bindings/nodejs): Align to OpenDAL exports #1466

Merged
merged 11 commits into from
Mar 5, 2023
3 changes: 2 additions & 1 deletion bindings/nodejs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ crate-type = ["cdylib"]

[dependencies]
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
napi = { version = "2.11.2", default-features = false, features = ["napi4", "async"] }
napi = { version = "2.11.2", default-features = false, features = ["napi6", "async"] }
napi-derive = "2.11.1"
opendal = { version = "0.29", path = "../../"}
chrono = "0.4.23"
futures = "0.3.26"
time = { version = "0.3.17", features = ["formatting"] }

[build-dependencies]
napi-build = "2"
Expand Down
73 changes: 65 additions & 8 deletions bindings/nodejs/__test__/index.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,77 @@

import test from 'ava'

import { OperatorFactory } from '../index.js'
import { Memory } from '../index.js'

test('test memory write & read', async (t) => {
let op = OperatorFactory.memory()
let builder = new Memory()
let op = builder.build()
suyanhanx marked this conversation as resolved.
Show resolved Hide resolved
let content = "hello world"
let path = 'test'

await op.write(path, Array.from(new TextEncoder().encode(content)))
let o = op.object(path)

let meta = await op.meta(path)
t.is(meta.size, content.length)
await o.write(new TextEncoder().encode(content))

let res = await op.read(path)
t.is(content, new TextDecoder().decode(Buffer.from(res)))
let meta = await o.stat()
t.is(meta.mode, 0)
suyanhanx marked this conversation as resolved.
Show resolved Hide resolved
t.is(meta.contentLength, BigInt(content.length))

await op.delete(path)
let res = await o.read()
t.is(content, new TextDecoder().decode(res))

await o.delete()
})


test('test memory write & read synchronously', (t) => {
let builder = new Memory()
let op = builder.build()
let content = "hello world"
let path = 'test'

let o = op.object(path)

o.writeSync(new TextEncoder().encode(content))

let meta = o.statSync()
t.is(meta.mode, 0)
t.is(meta.contentLength, BigInt(content.length))

let res = o.readSync()
t.is(content, new TextDecoder().decode(res))

o.deleteSync()
})

test('test scan', async (t) => {
let builder = new Memory()
let op = builder.build()
let content = "hello world"
let pathPrefix = 'test'
let paths = new Array(10).fill(0).map((_, index) => pathPrefix + index)
let objects = paths.map(p => op.object(p))

let writeTasks = objects.map((o) => new Promise(async (resolve, reject) => {
await o.write(new TextEncoder().encode(content))
resolve()
}))

await Promise.all(writeTasks)

let dir = op.object("")
let objList = await dir.scan()
let objectCount = 0
while (true) {
let o = await objList.next()
if (o === null) break
objectCount++
t.is(new TextDecoder().decode(await o.read()), content)
}

t.is(objectCount, paths.length)

objects.forEach(async (o) => {
await o.delete()
})
})
55 changes: 45 additions & 10 deletions bindings/nodejs/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,52 @@

/* auto-generated by NAPI-RS */

export class OperatorFactory {
static memory(): Operator
export const enum ObjectMode {
/** FILE means the object has data to read. */
FILE = 0,
/** DIR means the object can be listed. */
DIR = 1,
/** Unknown means we don't know what we can do on this object. */
Unknown = 2
}
export class ObjectMeta {
location: string
lastModified: number
size: number
export class Memory {
constructor()
build(): Operator
}
export class Operator {
meta(path: string): Promise<ObjectMeta>
write(path: string, content: Array<number>): Promise<void>
read(path: string): Promise<Array<number>>
delete(path: string): Promise<void>
object(path: string): DataObject
}
export class ObjectMetadata {
/** Mode of this object. */
get mode(): ObjectMode
/** Content-Disposition of this object */
get contentDisposition(): string | null
/** Content Length of this object */
get contentLength(): bigint | null
/** Content MD5 of this object. */
get contentMd5(): string | null
/**
* Content Range of this object.
* API undecided.
* Content Type of this object.
*/
get contentType(): string | null
/** ETag of this object. */
get etag(): string | null
/** Last Modified of this object.(UTC) */
get lastModified(): string | null
}
export class ObjectLister {
next(): Promise<DataObject | null>
}
export class DataObject {
suyanhanx marked this conversation as resolved.
Show resolved Hide resolved
stat(): Promise<ObjectMetadata>
statSync(): ObjectMetadata
write(content: Buffer): Promise<void>
writeSync(content: Buffer): void
read(): Promise<Buffer>
readSync(): Buffer
scan(): Promise<ObjectLister>
delete(): Promise<void>
deleteSync(): void
}
9 changes: 6 additions & 3 deletions bindings/nodejs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,11 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

const { OperatorFactory, ObjectMeta, Operator } = nativeBinding
const { Memory, Operator, ObjectMode, ObjectMetadata, ObjectLister, DataObject } = nativeBinding

module.exports.OperatorFactory = OperatorFactory
module.exports.ObjectMeta = ObjectMeta
module.exports.Memory = Memory
module.exports.Operator = Operator
module.exports.ObjectMode = ObjectMode
module.exports.ObjectMetadata = ObjectMetadata
module.exports.ObjectLister = ObjectLister
module.exports.DataObject = DataObject
1 change: 1 addition & 0 deletions bindings/nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"license": "Apache-2.0",
"devDependencies": {
"@napi-rs/cli": "^2.14.8",
"@types/node": "^18.14.5",
"ava": "^5.1.1"
},
"ava": {
Expand Down
Loading