You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by xu...@apache.org on 2023/03/26 11:59:14 UTC

[incubator-opendal] branch main updated: feat(bindings/nodejs): Support presign (#1772)

This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new 970e3bfe feat(bindings/nodejs): Support presign (#1772)
970e3bfe is described below

commit 970e3bfe2ff12838932e28360757d091177b9dd2
Author: Suyan <su...@gmail.com>
AuthorDate: Sun Mar 26 19:59:09 2023 +0800

    feat(bindings/nodejs): Support presign (#1772)
    
    * feat(bindings/nodejs): support presign
    
    Signed-off-by: suyanhanx <su...@gmail.com>
    
    * change header value to string
    
    Signed-off-by: suyanhanx <su...@gmail.com>
    
    * presign example
    
    Signed-off-by: suyanhanx <su...@gmail.com>
    
    ---------
    
    Signed-off-by: suyanhanx <su...@gmail.com>
---
 bindings/nodejs/README.md           |  4 --
 bindings/nodejs/examples/presign.js | 49 ++++++++++++++++++++++++
 bindings/nodejs/generated.js        |  9 ++++-
 bindings/nodejs/index.d.ts          | 30 +++++++++++++++
 bindings/nodejs/src/lib.rs          | 74 +++++++++++++++++++++++++++++++++++++
 5 files changed, 161 insertions(+), 5 deletions(-)

diff --git a/bindings/nodejs/README.md b/bindings/nodejs/README.md
index 58d084ad..f57fe91c 100644
--- a/bindings/nodejs/README.md
+++ b/bindings/nodejs/README.md
@@ -42,7 +42,6 @@ corepack enable
 
 `corepack` is distributed with Node.js, so you do not need to specifically look for a way to install it.
 
-
 ### Build
 
 ```bash
@@ -62,9 +61,6 @@ yarn test
 
 We use [`Cucumber`](https://cucumber.io/) for behavior testing. Refer to [here](https://cucumber.io/docs/guides/overview/) for more information about `Cucumber`.
 
-
-
 ## License
 
 Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
-
diff --git a/bindings/nodejs/examples/presign.js b/bindings/nodejs/examples/presign.js
new file mode 100644
index 00000000..b5826794
--- /dev/null
+++ b/bindings/nodejs/examples/presign.js
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const http = require('node:http')
+const url = require('node:url')
+const { Operator } = require('../index')
+
+const op = new Operator('s3', {
+  root: '/',
+  bucket: 'example-bucket',
+})
+
+const server = http.createServer(async (req, res) => {
+  res.setHeader('Content-Type', 'text/json; charset=utf-8')
+
+  if (req.url.startsWith('/presign') && req.method === 'GET') {
+    const urlParts = url.parse(req.url, true)
+    const path = urlParts.query.path
+    const expires = urlParts.query.expires
+
+    const presignedRequest = op.presignRead(path, parseInt(expires))
+
+    res.statusCode = 200
+    res.end(JSON.stringify({ url: presignedRequest.uri }))
+  } else {
+    res.statusCode = 404
+    res.end('Not Found')
+  }
+})
+
+server.listen(3000, () => {
+  console.log('Server is listening on port 3000.')
+})
diff --git a/bindings/nodejs/generated.js b/bindings/nodejs/generated.js
index c3c427b3..03d6bf6e 100644
--- a/bindings/nodejs/generated.js
+++ b/bindings/nodejs/generated.js
@@ -17,6 +17,12 @@
  * under the License.
  */
 
+/* tslint:disable */
+/* eslint-disable */
+/* prettier-ignore */
+
+/* auto-generated by NAPI-RS */
+
 const { existsSync, readFileSync } = require('fs')
 const { join } = require('path')
 
@@ -265,10 +271,11 @@ if (!nativeBinding) {
   throw new Error(`Failed to load native binding`)
 }
 
-const { Operator, Entry, Metadata, Lister, BlockingLister } = nativeBinding
+const { Operator, Entry, Metadata, Lister, BlockingLister, PresignedRequest } = nativeBinding
 
 module.exports.Operator = Operator
 module.exports.Entry = Entry
 module.exports.Metadata = Metadata
 module.exports.Lister = Lister
 module.exports.BlockingLister = BlockingLister
+module.exports.PresignedRequest = PresignedRequest
diff --git a/bindings/nodejs/index.d.ts b/bindings/nodejs/index.d.ts
index 4516d193..34cdbae3 100644
--- a/bindings/nodejs/index.d.ts
+++ b/bindings/nodejs/index.d.ts
@@ -64,6 +64,24 @@ export class Operator {
    * An error will be returned if given path doesn't end with `/`.
    */
   listSync(path: string): BlockingLister
+  /**
+   * Get a presigned request for read.
+   *
+   * Unit of expires is seconds.
+   */
+  presignRead(path: string, expires: number): PresignedRequest
+  /**
+   * Get a presigned request for write.
+   *
+   * Unit of expires is seconds.
+   */
+  presignWrite(path: string, expires: number): PresignedRequest
+  /**
+   * Get a presigned request for stat.
+   *
+   * Unit of expires is seconds.
+   */
+  presignStat(path: string, expires: number): PresignedRequest
 }
 export class Entry {
   /** Return the path of this entry. */
@@ -101,3 +119,15 @@ export class Lister {
 export class BlockingLister {
   next(): Entry | null
 }
+export class PresignedRequest {
+  /** Returns the HTTP method of this request. */
+  get method(): string
+  /** Returns the URI of this request. */
+  get uri(): string
+  /**
+   * Returns the headers of this request.
+   *
+   * The key of the map is the header name, and the value is the header value AS bytes.
+   */
+  headers(): Record<string, string>
+}
diff --git a/bindings/nodejs/src/lib.rs b/bindings/nodejs/src/lib.rs
index 2f062cdb..be3f7393 100644
--- a/bindings/nodejs/src/lib.rs
+++ b/bindings/nodejs/src/lib.rs
@@ -24,6 +24,7 @@ use std::str::FromStr;
 use futures::TryStreamExt;
 use napi::bindgen_prelude::*;
 use time::format_description::well_known::Rfc3339;
+use time::Duration;
 
 fn build_operator(
     scheme: opendal::Scheme,
@@ -206,6 +207,42 @@ impl Operator {
             self.0.blocking().scan(&path).map_err(format_napi_error)?,
         ))
     }
+
+    /// Get a presigned request for read.
+    ///
+    /// Unit of expires is seconds.
+    #[napi]
+    pub fn presign_read(&self, path: String, expires: u32) -> Result<PresignedRequest> {
+        let res = self
+            .0
+            .presign_read(&path, Duration::seconds(expires as i64))
+            .map_err(format_napi_error)?;
+        Ok(PresignedRequest(res))
+    }
+
+    /// Get a presigned request for write.
+    ///
+    /// Unit of expires is seconds.
+    #[napi]
+    pub fn presign_write(&self, path: String, expires: u32) -> Result<PresignedRequest> {
+        let res = self
+            .0
+            .presign_write(&path, Duration::seconds(expires as i64))
+            .map_err(format_napi_error)?;
+        Ok(PresignedRequest(res))
+    }
+
+    /// Get a presigned request for stat.
+    ///
+    /// Unit of expires is seconds.
+    #[napi]
+    pub fn presign_stat(&self, path: String, expires: u32) -> Result<PresignedRequest> {
+        let res = self
+            .0
+            .presign_stat(&path, Duration::seconds(expires as i64))
+            .map_err(format_napi_error)?;
+        Ok(PresignedRequest(res))
+    }
 }
 
 #[napi]
@@ -318,6 +355,43 @@ impl BlockingLister {
     }
 }
 
+#[napi]
+pub struct PresignedRequest(opendal::raw::PresignedRequest);
+
+#[napi]
+impl PresignedRequest {
+    /// Returns the HTTP method of this request.
+    #[napi(getter)]
+    pub fn method(&self) -> String {
+        self.0.method().to_string()
+    }
+
+    /// Returns the URI of this request.
+    #[napi(getter)]
+    pub fn uri(&self) -> String {
+        self.0.uri().to_string()
+    }
+
+    /// Returns the headers of this request.
+    ///
+    /// The key of the map is the header name, and the value is the header value.
+    #[napi]
+    pub fn headers(&self) -> HashMap<String, String> {
+        self.0
+            .header()
+            .iter()
+            .map(|(k, v)| {
+                (
+                    k.as_str().to_string(),
+                    v.to_str()
+                        .expect("header value contains non visible ascii characters")
+                        .to_string(),
+                )
+            })
+            .collect()
+    }
+}
+
 fn format_napi_error(err: opendal::Error) -> Error {
     Error::from_reason(format!("{}", err))
 }