feat: Add support for labels API to influxdb_client

List all Labels
Retrieve a label by ID
Create a Label
Update a Label
Delete a Label

Add examples and test
Add necessary models

Signed-off-by: jeivardan <jeivardanvenkatesh@gmail.com>
pull/24376/head
jeivardan 2021-03-30 07:39:08 +05:30
parent 5750a69c75
commit ba5935f0bd
4 changed files with 430 additions and 0 deletions

View File

@ -0,0 +1,26 @@
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let influx_url = "http://localhost:8888";
let token = "some-token";
let client = influxdb2_client::Client::new(influx_url, token);
println!("{:?}", client.find_labels().await?);
println!("{:?}", client.find_label_by_id("some-label_id").await?);
let mut properties = std::collections::HashMap::new();
properties.insert("some-key".to_string(), "some-value".to_string());
println!(
"{:?}",
client
.create_label("some-org_id", "some-name", Some(properties))
.await?
);
println!(
"{:?}",
client
.update_label(Some("some-name".to_string()), None, "some-label_id")
.await?
);
println!("{:?}", client.delete_label("some-label_id").await?);
Ok(())
}

View File

@ -0,0 +1,268 @@
use crate::models::{LabelCreateRequest, LabelResponse, LabelUpdate, LabelsResponse};
use crate::{Client, Http, RequestError, ReqwestProcessing, Serializing};
use reqwest::{Method, StatusCode};
use snafu::ResultExt;
impl Client {
/// List all Labels
pub async fn find_labels(&self) -> Result<LabelsResponse, RequestError> {
let labels_url = format!("{}/api/v2/labels", self.url);
let response = self
.request(Method::GET, &labels_url)
.send()
.await
.context(ReqwestProcessing)?;
match response.status() {
StatusCode::OK => Ok(response
.json::<LabelsResponse>()
.await
.context(ReqwestProcessing)?),
status => {
let text = response.text().await.context(ReqwestProcessing)?;
Http { status, text }.fail()?
}
}
}
/// Retrieve a label by ID
pub async fn find_label_by_id(&self, label_id: &str) -> Result<LabelResponse, RequestError> {
let labels_by_id_url = format!("{}/api/v2/labels/{}", self.url, label_id);
let response = self
.request(Method::GET, &labels_by_id_url)
.send()
.await
.context(ReqwestProcessing)?;
match response.status() {
StatusCode::OK => Ok(response
.json::<LabelResponse>()
.await
.context(ReqwestProcessing)?),
status => {
let text = response.text().await.context(ReqwestProcessing)?;
Http { status, text }.fail()?
}
}
}
/// Create a Label
pub async fn create_label(
&self,
org_id: &str,
name: &str,
properties: Option<::std::collections::HashMap<String, String>>,
) -> Result<LabelResponse, RequestError> {
let create_label_url = format!("{}/api/v2/labels", self.url);
let body = LabelCreateRequest {
org_id: org_id.into(),
name: name.into(),
properties,
};
let response = self
.request(Method::POST, &create_label_url)
.body(serde_json::to_string(&body).context(Serializing)?)
.send()
.await
.context(ReqwestProcessing)?;
match response.status() {
StatusCode::CREATED => Ok(response
.json::<LabelResponse>()
.await
.context(ReqwestProcessing)?),
status => {
let text = response.text().await.context(ReqwestProcessing)?;
Http { status, text }.fail()?
}
}
}
/// Update a Label
pub async fn update_label(
&self,
name: Option<String>,
properties: Option<::std::collections::HashMap<String, String>>,
label_id: &str,
) -> Result<LabelResponse, RequestError> {
let update_label_url = format!("{}/api/v2/labels/{}", &self.url, label_id);
let body = LabelUpdate { name, properties };
let response = self
.request(Method::PATCH, &update_label_url)
.body(serde_json::to_string(&body).context(Serializing)?)
.send()
.await
.context(ReqwestProcessing)?;
match response.status() {
StatusCode::OK => Ok(response
.json::<LabelResponse>()
.await
.context(ReqwestProcessing)?),
status => {
let text = response.text().await.context(ReqwestProcessing)?;
Http { status, text }.fail()?
}
}
}
/// Delete a Label
pub async fn delete_label(&self, label_id: &str) -> Result<(), RequestError> {
let delete_label_url = format!("{}/api/v2/labels/{}", &self.url, label_id);
let response = self
.request(Method::DELETE, &delete_label_url)
.send()
.await
.context(ReqwestProcessing)?;
match response.status() {
StatusCode::NO_CONTENT => Ok(()),
status => {
let text = response.text().await.context(ReqwestProcessing)?;
Http { status, text }.fail()?
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use mockito::mock;
type Error = Box<dyn std::error::Error>;
type Result<T = (), E = Error> = std::result::Result<T, E>;
#[tokio::test]
async fn find_labels() -> Result {
let token = "some-token";
let mock_server = mock("GET", "/api/v2/labels")
.match_header("Authorization", format!("Token {}", token).as_str())
.create();
let client = Client::new(&mockito::server_url(), token);
let _result = client.find_labels().await;
mock_server.assert();
Ok(())
}
#[tokio::test]
async fn find_label_by_id() -> Result {
let token = "some-token";
let label_id = "some-id";
let mock_server = mock("GET", format!("/api/v2/labels/{}", label_id).as_str())
.match_header("Authorization", format!("Token {}", token).as_str())
.create();
let client = Client::new(&mockito::server_url(), token);
let _result = client.find_label_by_id(label_id).await;
mock_server.assert();
Ok(())
}
#[tokio::test]
async fn create_label() -> Result {
let token = "some-token";
let org_id = "some-org";
let name = "some-user";
let mut properties = std::collections::HashMap::new();
properties.insert("some-key".to_string(), "some-value".to_string());
let mock_server = mock("POST", "/api/v2/labels")
.match_header("Authorization", format!("Token {}", token).as_str())
.match_body(
format!(
r#"{{"org_id":"{}","name":"{}","properties":"{:?}"}}"#,
org_id, name, properties
)
.as_str(),
)
.create();
let client = Client::new(&mockito::server_url(), token);
let _result = client.create_label(org_id, name, Some(properties)).await;
mock_server.assert();
Ok(())
}
#[tokio::test]
async fn create_label_opt() -> Result {
let token = "some-token";
let org_id = "some-org_id";
let name = "some-user";
let mock_server = mock("POST", "/api/v2/labels")
.match_header("Authorization", format!("Token {}", token).as_str())
.match_body(format!(r#"{{"org_id":"{}","name":"{}"}}"#, org_id, name).as_str())
.create();
let client = Client::new(&mockito::server_url(), token);
let _result = client.create_label(org_id, name, None).await;
mock_server.assert();
Ok(())
}
#[tokio::test]
async fn update_label() -> Result {
let token = "some-token";
let name = "some-user";
let label_id = "some-label_id";
let mut properties = std::collections::HashMap::new();
properties.insert("some-key".to_string(), "some-value".to_string());
let mock_server = mock("PATCH", format!("/api/v2/labels/{}", label_id).as_str())
.match_header("Authorization", format!("Token {}", token).as_str())
.match_body(
format!(r#"{{"name":"{}","properties":"{:?}"}}"#, name, properties).as_str(),
)
.create();
let client = Client::new(&mockito::server_url(), token);
let _result = client
.update_label(Some(name.to_string()), Some(properties), label_id)
.await;
mock_server.assert();
Ok(())
}
#[tokio::test]
async fn update_label_opt() -> Result {
let token = "some-token";
let label_id = "some-label_id";
let mock_server = mock("PATCH", format!("/api/v2/labels/{}", label_id).as_str())
.match_header("Authorization", format!("Token {}", token).as_str())
.match_body("{}")
.create();
let client = Client::new(&mockito::server_url(), token);
let _result = client.update_label(None, None, label_id).await;
mock_server.assert();
Ok(())
}
#[tokio::test]
async fn delete_label() -> Result {
let token = "some-token";
let label_id = "some-label_id";
let mock_server = mock("DELETE", format!("/api/v2/labels/{}", label_id).as_str())
.match_header("Authorization", format!("Token {}", token).as_str())
.create();
let client = Client::new(&mockito::server_url(), token);
let _result = client.delete_label(label_id).await;
mock_server.assert();
Ok(())
}
}

View File

@ -0,0 +1,108 @@
use serde::{Deserialize, Serialize};
/// Post create label request, to create a new label
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LabelCreateRequest {
/// Organisation ID
#[serde(rename = "orgID")]
pub org_id: String,
/// Organisation name
pub name: String,
/// Key/Value pairs associated with properties.
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<::std::collections::HashMap<String, String>>,
}
impl LabelCreateRequest {
/// Return instance of LabelCreateRequest
pub fn new(org_id: String, name: String) -> Self {
Self {
org_id,
name,
..Default::default()
}
}
}
/// LabelResponse
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LabelResponse {
/// Label
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<crate::models::Label>,
/// Links
#[serde(skip_serializing_if = "Option::is_none")]
pub links: Option<crate::models::Links>,
}
impl LabelResponse {
/// Returns instance of LabelResponse
pub fn new() -> Self {
Self::default()
}
}
///LabelsResponse
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LabelsResponse {
/// Labels
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub labels: Vec<crate::models::Label>,
/// Links
#[serde(skip_serializing_if = "Option::is_none")]
pub links: Option<crate::models::Links>,
}
impl LabelsResponse {
/// Returns List of Labels
pub fn new() -> Self {
Self::default()
}
}
///LabelUpdateRequest
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LabelUpdate {
/// Name
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
/// Key/Value pairs associated with properties.
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<::std::collections::HashMap<String, String>>,
}
impl LabelUpdate {
/// Returns an instance of LabelUpdate
pub fn new() -> Self {
Self::default()
}
}
/// Label
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Label {
/// Label ID
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
/// Org ID
#[serde(rename = "orgID", skip_serializing_if = "Option::is_none")]
pub org_id: Option<String>,
/// Label name
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
/// Key/Value pairs associated with properties.
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<::std::collections::HashMap<String, String>>,
}
impl Label {
/// Returns an instance of Label
pub fn new() -> Self {
Self::default()
}
}

View File

@ -0,0 +1,28 @@
//! Links
use serde::{Deserialize, Serialize};
/// Links
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Links {
/// URI of resource.
#[serde(skip_serializing_if = "Option::is_none")]
pub next: Option<String>,
/// URI of resource.
#[serde(rename = "self")]
pub self_: String,
/// URI of resource.
#[serde(skip_serializing_if = "Option::is_none")]
pub prev: Option<String>,
}
impl Links {
/// Returns instance of Links
pub fn new(self_: String) -> Self {
Self {
self_,
..Default::default()
}
}
}