2 个版本

0.1.1 2020 年 9 月 20 日
0.1.0 2020 年 9 月 20 日

#3 in #reason

MIT/Apache

75KB
813

tenable

Crates.io Crates.io Crates.io

这是一个 Tenable API 抽象化。

API 本身太大,一个人难以开发。这就是为什么这个包不提供所有端点的接口,而是专注于模块化和可扩展性。这个包不是为所有端点提供接口,而是尽可能让用户添加自己的端点,并希望之后能贡献它们。

用法

将这个包添加到你的 Cargo.toml 作为依赖项。之后,你可以像这样使用它来执行 API 调用,例如获取所有资产:

同步

use std::convert::Infallible;
use reqwest::blocking::Client;
use tenable::{requests::AssetReq, Error, Response, Tenable};
use http::Request;

pub fn request(req: Request<Vec<u8>>) -> Result<Response, Error<reqwest::Error>> {
    let (req, body) = req.into_parts();
    let res = Client::new()
        .request(req.method, &req.uri.to_string())
        .headers(req.headers)
        .body(body)
        .send()
        .map_err(Error::Request)?;
    Ok(Response {
        status: res.status(),
        body: res.bytes().map_err(Error::Request)?,
    })
}

let tenable = Tenable::new(
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000000000",
);
let req = tenable.assets();
let _assets = Tenable::request(req, request).expect("Unable to list all assets");

异步

use std::convert::Infallible;
use reqwest::Client;
use tenable::{requests::AssetReq, types::Assets, Error, Response, Tenable};
use http::Request;

pub async fn request_async(req: Request<Vec<u8>>) -> Result<Response, Error<reqwest::Error>> {
   let (req, body) = req.into_parts();
   let res = Client::new()
       .request(req.method, &req.uri.to_string())
       .headers(req.headers)
       .body(body)
       .send()
       .await
       .map_err(Error::Request)?;
   Ok(Response {
       status: res.status(),
       body: res.bytes().await.map_err(Error::Request)?,
   })
}

let tenable = Tenable::new(
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000000000",
);
let req = tenable.assets();
let _assets: Assets = Tenable::request_async(req, request_async).await
    .expect("Unable to list all assets");

扩展

可以通过创建一个实现 HttpRequest 的类型来扩展功能,该类型定义了请求的样式以及如何处理服务器响应。以下是如何使用处理 /assets 端点的 AssetsReq 类型来实现的示例:

use http::{header::HeaderValue, status::StatusCode, Method, Request};
use tenable::{
   types::Assets,
   Error, HttpRequest, Response, Tenable,
};
use std::fmt;

#[derive(Clone, Debug)]
pub struct AssetsReq<'a> {
    pub tenable: &'a Tenable<'a>,
}

impl<RE: fmt::Debug> HttpRequest<RE> for AssetsReq<'_> {
    // The final concret type returned on a successful call
    type Output = Assets;

    #[inline]
    fn to_request(&self) -> Result<Request<Vec<u8>>, Error<RE>> {
        // Create a request...
        let req = Request::builder()
            // ...by specificing the endpoint...
            .uri(format!("{}/assets", self.tenable.uri))
            // ...the method...
            .method(Method::GET)
            // ...authorization...
            .header(
                "X-ApiKeys",
                HeaderValue::from_str(self.tenable.auth.as_ref())?,
            )
            // ...and more like required headers, form parameters, body...
            .header("Accept", HeaderValue::from_static("application/json"))
            .body(Vec::new())?;
        Ok(req)
    }

    #[inline]
    fn from_response(&self, res: Response) -> Result<Self::Output, Error<RE>> {
        // Handles the server response
        match res.status {
            // When the call was successfull, continue with deserializing
            StatusCode::OK => Ok(serde_json::from_slice(&res.body)?),
            // Otherwise, check whether the server returned one of the known errors
            StatusCode::FORBIDDEN => Err(Error::InsufficientPermission),
            StatusCode::TOO_MANY_REQUESTS => Err(Error::RateLimitReached),
            // Every other error may be collected in catch all type
            code => Err(Error::UnexpectedStatusCode(code)),
        }
    }
}

为了能够直接使用这个类型与 tenable 结构一起,我们可以添加一个新的特质并为 tenable 实现:

use tenable::{Tenable, types::AssetsReq};

pub trait AssetReq {
    fn assets(&self) -> AssetsReq<'_>;
}

impl AssetReq for Tenable<'_> {
    fn assets(&self) -> AssetsReq<'_> {
        AssetsReq { tenable: self }
    }
}

许可协议

许可协议为以下之一:

任选其一。

贡献

除非你明确表示,否则根据 Apache-2.0 许可证定义的,你提交的任何贡献都应按上述方式双许可,不得附加任何额外条款或条件。

许可协议:MIT OR Apache-2.0

依赖

~1.1–2MB
~41K SLoC