2 个版本
0.1.1 | 2020 年 9 月 20 日 |
---|---|
0.1.0 | 2020 年 9 月 20 日 |
#3 in #reason
75KB
813 行
tenable
这是一个 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 (LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT 许可协议 (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
任选其一。
贡献
除非你明确表示,否则根据 Apache-2.0 许可证定义的,你提交的任何贡献都应按上述方式双许可,不得附加任何额外条款或条件。
许可协议:MIT OR Apache-2.0
依赖
~1.1–2MB
~41K SLoC