Skip to content

Commit

Permalink
Use Url instead of str (#3)
Browse files Browse the repository at this point in the history
* Use Url instead of str

* Ducument usage of Url

* Add trailing slash to metrics job path

* Use trailing slashes for url build up

* Make MetricsPusher debuggable

* Do not add trailing slash to url
  • Loading branch information
maoertel authored Nov 14, 2023
1 parent 6898123 commit 58d7630
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 49 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ features = ["blocking", "with_reqwest", "with_reqwest_blocking"]
[dependencies]
async-trait = "0.1"
prometheus = "0.13"
url = "2.4"
reqwest = { version = "0.11", default-features = false, optional = true }
log = { version = "0.4", default-features = false, optional = true }

Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ use prometheus::labels;
use prometheus_push::with_request::PushClient;
use prometheus_push::MetricsPusher;
use reqwest::Client;
use url::Url;

let push_gateway = "<address to your instance>";
let metrics_pusher = MetricsPusher::<PushClient>::from(Client::new(), &push_gateway);
let push_gateway: Url = <address to your instance>;
let metrics_pusher = MetricsPusher::<PushClient>::from(Client::new(), &push_gateway)?;
metrics_pusher
.push_all(
"<your push jobs name>",
Expand All @@ -39,18 +40,19 @@ Basically it is as simple as that.

```rust
use prometheus_push::Push;
...

pub struct YourClient {
...
}

#[async_trait::async_trait]
impl Push for YourClient {
async fn push_all(&self, url: &str, body: Vec<u8>, content_type: &str) -> Result<()> {
async fn push_all(&self, url: &Url, body: Vec<u8>, content_type: &str) -> Result<()> {
// implement a PUT request with your client with this body and `content_type` in header
}

async fn push_add(&self, url: &str, body: Vec<u8>, content_type: &str) -> Result<()> {
async fn push_add(&self, url: &Url, body: Vec<u8>, content_type: &str) -> Result<()> {
// implement a POST request with your client with this body and `content_type` in header
}
}
Expand Down
18 changes: 10 additions & 8 deletions src/blocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,35 @@ use prometheus::proto::MetricFamily;
use prometheus::Encoder;
#[cfg(feature = "with_reqwest_blocking")]
use reqwest::blocking::Client;
use url::Url;

#[cfg(feature = "with_reqwest_blocking")]
use crate::blocking::with_request::PushClient;
use crate::error::Result;
use crate::helper::create;
use crate::helper::create_metrics_job_url;
use crate::helper::metric_families_from;
use crate::helper::validate_url;
use crate::PushType;

pub trait Push {
fn push_all(&self, url: &str, body: Vec<u8>, content_type: &str) -> Result<()>;
fn push_add(&self, url: &str, body: Vec<u8>, content_type: &str) -> Result<()>;
fn push_all(&self, url: &Url, body: Vec<u8>, content_type: &str) -> Result<()>;
fn push_add(&self, url: &Url, body: Vec<u8>, content_type: &str) -> Result<()>;
}

#[derive(Debug)]
pub struct MetricsPusher<P: Push> {
push_client: P,
url: String,
url: Url,
}

impl<P: Push> MetricsPusher<P> {
pub fn new(push_client: P, url: &str) -> MetricsPusher<P> {
let url = validate_url(url);
Self { push_client, url }
pub fn new(push_client: P, url: &Url) -> Result<MetricsPusher<P>> {
let url = create_metrics_job_url(url)?;
Ok(Self { push_client, url })
}

#[cfg(feature = "with_reqwest_blocking")]
pub fn from(client: Client, url: &str) -> MetricsPusher<PushClient> {
pub fn from(client: Client, url: &Url) -> Result<MetricsPusher<PushClient>> {
MetricsPusher::new(PushClient::new(client), url)
}

Expand Down
12 changes: 7 additions & 5 deletions src/blocking/with_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ use reqwest::blocking::Client;
use reqwest::blocking::Response;
use reqwest::header::CONTENT_TYPE;
use reqwest::StatusCode;
use url::Url;

use crate::blocking::Push;
use crate::error::Result;
use crate::helper::handle_response;
use crate::helper::Respond;

#[derive(Debug)]
pub struct PushClient {
client: Client,
}
Expand All @@ -19,21 +21,21 @@ impl PushClient {
}

impl Push for PushClient {
fn push_all(&self, url: &str, body: Vec<u8>, content_type: &str) -> Result<()> {
fn push_all(&self, url: &Url, body: Vec<u8>, content_type: &str) -> Result<()> {
let response = &self
.client
.put(url)
.put(url.as_str())
.header(CONTENT_TYPE, content_type)
.body(body)
.send()?;

handle_response(response)
}

fn push_add(&self, url: &str, body: Vec<u8>, content_type: &str) -> Result<()> {
fn push_add(&self, url: &Url, body: Vec<u8>, content_type: &str) -> Result<()> {
let response = &self
.client
.post(url)
.post(url.as_str())
.header(CONTENT_TYPE, content_type)
.body(body)
.send()?;
Expand All @@ -47,7 +49,7 @@ impl Respond for Response {
self.status()
}

fn get_url(&self) -> &reqwest::Url {
fn get_url(&self) -> &Url {
self.url()
}
}
8 changes: 8 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub type Result<T> = std::result::Result<T, PushMetricsError>;
#[derive(Debug)]
pub enum PushMetricsError {
Prometheus(prometheus::Error),
Url(url::ParseError),
Generic(String),
#[cfg(any(feature = "with_reqwest", feature = "with_reqwest_blocking"))]
Reqwest(reqwest::Error),
Expand All @@ -17,6 +18,7 @@ impl fmt::Display for PushMetricsError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
PushMetricsError::Prometheus(e) => std::fmt::Display::fmt(e, f),
PushMetricsError::Url(e) => std::fmt::Display::fmt(e, f),
PushMetricsError::Generic(e) => std::fmt::Display::fmt(e, f),
#[cfg(any(feature = "with_reqwest", feature = "with_reqwest_blocking"))]
PushMetricsError::Reqwest(e) => std::fmt::Display::fmt(e, f),
Expand All @@ -30,6 +32,12 @@ impl From<prometheus::Error> for PushMetricsError {
}
}

impl From<url::ParseError> for PushMetricsError {
fn from(error: url::ParseError) -> Self {
PushMetricsError::Url(error)
}
}

#[cfg(any(feature = "with_reqwest", feature = "with_reqwest_blocking"))]
impl From<reqwest::Error> for PushMetricsError {
fn from(error: reqwest::Error) -> Self {
Expand Down
30 changes: 11 additions & 19 deletions src/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,24 @@ use prometheus::ProtobufEncoder;
use prometheus::Registry;
#[cfg(any(feature = "with_reqwest", feature = "with_reqwest_blocking"))]
use reqwest::StatusCode;
use url::Url;

use crate::error::PushMetricsError;
use crate::error::Result;

const LABEL_NAME_JOB: &str = "job";
const METRICS_JOB_PATH: &str = "metrics/job/";

pub(crate) fn validate_url(url: &str) -> String {
let mut slash = "/";
let mut scheme = "http://";
if url.contains("://") {
scheme = ""
};
if url.ends_with('/') {
slash = ""
};

format!("{scheme}{url}{slash}metrics/job")
pub(crate) fn create_metrics_job_url(url: &Url) -> Result<Url> {
Ok(url.join(METRICS_JOB_PATH)?)
}

pub(crate) fn create<'a, BH: BuildHasher>(
job: &'a str,
url: &'a str,
url: &'a Url,
grouping: &HashMap<&str, &str, BH>,
metric_families: Vec<MetricFamily>,
) -> Result<(String, Vec<u8>, ProtobufEncoder)> {
) -> Result<(Url, Vec<u8>, ProtobufEncoder)> {
let job = validate_job(job)?;
let url = build_url(url, job, grouping)?;
let encoder = ProtobufEncoder::new();
Expand Down Expand Up @@ -63,11 +56,12 @@ fn validate_job(job: &str) -> Result<&str> {
}

fn build_url<'a, BH: BuildHasher>(
url: &'a str,
url: &'a Url,
job: &'a str,
grouping: &'a HashMap<&'a str, &'a str, BH>,
) -> Result<String> {
) -> Result<Url> {
let mut url_params = vec![job];

for (label_name, label_value) in grouping {
if label_value.contains('/') {
return Err(PushMetricsError::Generic(format!(
Expand All @@ -78,9 +72,7 @@ fn build_url<'a, BH: BuildHasher>(
url_params.push(label_value);
}

let url = format!("{url}/{params}", params = url_params.join("/"));

Ok(url)
Ok(url.join(&url_params.join("/"))?)
}

fn encode_metrics<'a, BH: BuildHasher>(
Expand Down Expand Up @@ -119,7 +111,7 @@ fn encode_metrics<'a, BH: BuildHasher>(
#[cfg(any(feature = "with_reqwest", feature = "with_reqwest_blocking"))]
pub(crate) trait Respond {
fn get_status_code(&self) -> StatusCode;
fn get_url(&self) -> &reqwest::Url;
fn get_url(&self) -> &Url;
}

#[cfg(any(feature = "with_reqwest", feature = "with_reqwest_blocking"))]
Expand Down
18 changes: 10 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,40 @@ use prometheus::proto::MetricFamily;
use prometheus::Encoder;
#[cfg(feature = "with_reqwest")]
use reqwest::Client;
use url::Url;

use crate::error::Result;
use crate::helper::create;
use crate::helper::create_metrics_job_url;
use crate::helper::metric_families_from;
use crate::helper::validate_url;
#[cfg(feature = "with_reqwest")]
use crate::with_request::PushClient;

#[async_trait::async_trait]
pub trait Push {
async fn push_all(&self, url: &str, body: Vec<u8>, content_type: &str) -> Result<()>;
async fn push_add(&self, url: &str, body: Vec<u8>, content_type: &str) -> Result<()>;
async fn push_all(&self, url: &Url, body: Vec<u8>, content_type: &str) -> Result<()>;
async fn push_add(&self, url: &Url, body: Vec<u8>, content_type: &str) -> Result<()>;
}

enum PushType {
Add,
All,
}

#[derive(Debug)]
pub struct MetricsPusher<P: Push> {
push_client: P,
url: String,
url: Url,
}

impl<P: Push> MetricsPusher<P> {
pub fn new(push_client: P, url: &str) -> MetricsPusher<P> {
let url = validate_url(url);
Self { push_client, url }
pub fn new(push_client: P, url: &Url) -> Result<MetricsPusher<P>> {
let url = create_metrics_job_url(url)?;
Ok(Self { push_client, url })
}

#[cfg(feature = "with_reqwest")]
pub fn from(client: Client, url: &str) -> MetricsPusher<PushClient> {
pub fn from(client: Client, url: &Url) -> Result<MetricsPusher<PushClient>> {
MetricsPusher::new(PushClient::new(client), url)
}

Expand Down
12 changes: 7 additions & 5 deletions src/with_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use reqwest::header::CONTENT_TYPE;
use reqwest::Client;
use reqwest::Response;
use reqwest::StatusCode;
use url::Url;

#[derive(Debug)]
pub struct PushClient {
client: Client,
}
Expand All @@ -19,10 +21,10 @@ impl PushClient {

#[async_trait::async_trait]
impl Push for PushClient {
async fn push_all(&self, url: &str, body: Vec<u8>, content_type: &str) -> Result<()> {
async fn push_all(&self, url: &Url, body: Vec<u8>, content_type: &str) -> Result<()> {
let response = &self
.client
.put(url)
.put(url.as_str())
.header(CONTENT_TYPE, content_type)
.body(body)
.send()
Expand All @@ -31,10 +33,10 @@ impl Push for PushClient {
handle_response(response)
}

async fn push_add(&self, url: &str, body: Vec<u8>, content_type: &str) -> Result<()> {
async fn push_add(&self, url: &Url, body: Vec<u8>, content_type: &str) -> Result<()> {
let response = &self
.client
.post(url)
.post(url.as_str())
.header(CONTENT_TYPE, content_type)
.body(body)
.send()
Expand All @@ -49,7 +51,7 @@ impl Respond for Response {
self.status()
}

fn get_url(&self) -> &reqwest::Url {
fn get_url(&self) -> &Url {
self.url()
}
}

0 comments on commit 58d7630

Please sign in to comment.