Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add annitation support #60

Merged
merged 8 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,13 @@ jobs:
url: TESTING_DEV_API_URL
token: TESTING_DEV_TOKEN
org_id: TESTING_DEV_ORG_ID
flags: --features integration-tests
- environment: staging
url: TESTING_STAGING_API_URL
token: TESTING_STAGING_TOKEN
org_id: TESTING_STAGING_ORG_ID
flags: --features integration-tests

steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
Expand Down
21 changes: 13 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,25 @@ include = ["src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
resolver = "2"

[dependencies]
reqwest = { version = "0.11", default-features = false, features = ["json", "stream", "gzip", "blocking"] }
reqwest = { version = "0.12", default-features = false, features = [
"json",
"stream",
"gzip",
"blocking",
] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
chrono = { version = "0.4", features = ["serde"] }
serde_qs = "0.8"
serde_qs = "0.13"
thiserror = "1"
bytes = "1"
flate2 = "1"
http = "0.2"
http = "1"
backoff = { version = "0.4", features = ["futures"] }
futures = "0.3"
tokio = { version = "1", optional = true, features = ["rt", "sync"] }
async-std = { version = "1", optional = true, features = ["tokio1"] }
url = "2"
url = { version = "2", features = ["serde"] }
tracing = { version = "0.1" }
tokio-stream = "0.1"
bitflags = "2"
Expand All @@ -37,11 +42,9 @@ bitflags_serde_shim = "0.2.4"
[dev-dependencies]
tokio = { version = "1", features = ["full"] }
async-std = { version = "1", features = ["attributes"] }
serde_test = "1"
test-context = "0.1"
async-trait = "0.1"
test-context = "0.3"
futures-util = "0.3"
httpmock = "0.6"
httpmock = "0.7"
structopt = "0.3"
tracing-subscriber = { version = "0.3", features = ["ansi", "env-filter"] }

Expand All @@ -52,3 +55,5 @@ async-std = ["backoff/async-std", "dep:async-std"]
default-tls = ["reqwest/default-tls"]
native-tls = ["reqwest/native-tls"]
rustls-tls = ["reqwest/rustls-tls"]
# require a set uo environment variable to run the integration tests
integration-tests = []
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ For more information check out the [official documentation](https://axiom.co/doc

## Quickstart

Add the following to your Cargo.toml:
Add the following to your `Cargo.toml`:

```toml
[dependencies]
Expand All @@ -30,14 +30,14 @@ axiom-rs = "0.9"
If you use the [Axiom CLI](https://github.com/axiomhq/cli), run
`eval $(axiom config export -f)` to configure your environment variables.

Otherwise create a personal token in
Otherwise, create a personal token in
[the Axiom settings](https://cloud.axiom.co/profile) and make note of
the organization ID from the settings page of the organization you want to
access.

Create and use a client like this:

```rust
```rust,no_run
use axiom_rs::Client;
use serde_json::json;

Expand All @@ -53,7 +53,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// AXIOM_TOKEN and AXIOM_ORG_ID:
let client = Client::new()?;

client.datasets.create("my-dataset", "").await?;
client.datasets().create("my-dataset", "").await?;

client
.ingest(
Expand All @@ -69,7 +69,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.await?;
println!("{:?}", res);

client.datasets.delete("my-dataset").await?;
client.datasets().delete("my-dataset").await?;
Ok(())
}
```
Expand All @@ -82,12 +82,12 @@ The following are a list of
[Cargo features](https://doc.rust-lang.org/stable/cargo/reference/features.html#the-features-section)
that can be enabled or disabled:

- **default-tls** _(enabled by default)_: Provides TLS support to connect
- **`default-tls`** _(enabled by default)_: Provides TLS support to connect
over HTTPS.
- **native-tls**: Enables TLS functionality provided by `native-tls`.
- **rustls-tls**: Enables TLS functionality provided by `rustls`.
- **tokio** _(enabled by default)_: Enables the usage with the `tokio` runtime.
- **async-std** : Enables the usage with the `async-std` runtime.
- **`native-tls`**: Enables TLS functionality provided by `native-tls`.
- **`rustls-tls`**: Enables TLS functionality provided by `rustls`.
- **`tokio`** _(enabled by default)_: Enables the usage with the `tokio` runtime.
- **`async-std`**: Enables the usage with the `async-std` runtime.

## License

Expand Down
18 changes: 9 additions & 9 deletions examples/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ enum Datasets {
List,
/// Get a dataset
Get { name: String },
/// Get information for a dataset
Info { name: String },
// /// Get information for a dataset
// Info { name: String },
/// Update the description of a dataset
Update {
name: String,
Expand Down Expand Up @@ -65,24 +65,24 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
match opt {
Opt::Datasets(datasets) => match datasets {
Datasets::List => client
.datasets
.datasets()
.list()
.await?
.into_iter()
.for_each(|dataset| {
println!("{:?}", dataset);
}),
Datasets::Get { name } => println!("{:?}", client.datasets.get(&name).await?),
Datasets::Info { name } => println!("{:?}", client.datasets.info(&name).await?),
Datasets::Get { name } => println!("{:?}", client.datasets().get(&name).await?),
// Datasets::Info { name } => println!("{:?}", client.datasets().info(&name).await?),
Datasets::Update { name, description } => {
let dataset = client.datasets.update(&name, description).await?;
let dataset = client.datasets().update(&name, description).await?;
println!("{:?}", dataset);
}
Datasets::Delete { name } => client.datasets.delete(&name).await?,
Datasets::Delete { name } => client.datasets().delete(&name).await?,
Datasets::Trim { name, seconds } => println!(
"{:?}",
client
.datasets
.datasets()
.trim(&name, Duration::from_secs(seconds))
.await?
),
Expand All @@ -105,7 +105,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
},
Opt::Users(users) => match users {
Users::Current => {
let user = client.users.current().await?;
let user = client.users().current().await?;
println!("{:?}", user);
}
},
Expand Down
37 changes: 37 additions & 0 deletions src/annotations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//! Manage datasets, ingest data and query it.
//!
//! You're probably looking for the [`Client`].
//!
//! # Examples
//! ```no_run
//! use axiom_rs::{Client, Error, annotations::requests};
//! use serde_json::json;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Error> {
//! let client = Client::new()?;
//!
//! let req = requests::Create::builder()
//! .with_type("cake")?
//! .with_datasets(vec!["snot".to_string(), "badger".to_string()])?
//! .with_title("cookie")
//! .build();
//! client.annotations().create(req).await?;
//!
//! let res = client.annotations().list(requests::List::default()).await?;
//! assert_eq!(1, res.len());
//!
//! client.annotations().delete(&res[1].id).await?;
//!
//! Ok(())
//! }
//! ```
//!
mod client;
mod model;
pub mod requests;
#[cfg(test)]
mod tests;

pub use client::Client;
pub use model::Annotation;
85 changes: 85 additions & 0 deletions src/annotations/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use std::fmt;

use crate::{annotations::Annotation, error::Result, http};
use tracing::instrument;

use super::requests;

/// Provides methods to work with Axiom annotations.
#[derive(Debug, Clone)]
pub struct Client<'client> {
http_client: &'client http::Client,
}

impl<'client> Client<'client> {
pub(crate) fn new(http_client: &'client http::Client) -> Self {
Self { http_client }
}

/// Creates an annotation
///
/// # Errors
/// If the API call fails
#[instrument(skip(self))]
pub async fn create(&self, req: requests::Create) -> Result<Annotation> {
self.http_client
.post("/v2/annotations", req)
.await?
.json()
.await
}

/// Gets an annotation
///
/// # Errors
/// If the API call fails
#[instrument(skip(self))]
pub async fn get(&self, id: impl fmt::Display + fmt::Debug) -> Result<Annotation> {
self.http_client
.get(format!("/v2/annotations/{id}"))
.await?
.json()
.await
}

/// Lists annotations
///
/// # Errors
/// If the API call fails
#[instrument(skip(self))]
pub async fn list(&self, req: requests::List) -> Result<Vec<Annotation>> {
let query_params = serde_qs::to_string(&req)?;
self.http_client
.get(format!("/v2/annotations?{query_params}"))
.await?
.json()
.await
}
Licenser marked this conversation as resolved.
Show resolved Hide resolved

/// Updates an annotation
///
/// # Errors
/// If the API call fails
#[instrument(skip(self))]
pub async fn update(
&self,
id: impl fmt::Display + fmt::Debug,
req: requests::Update,
) -> Result<Annotation> {
self.http_client
.put(format!("/v2/annotations/{id}"), req)
.await?
.json()
.await
}
/// Delets an annotation
///
/// # Errors
/// If the API call fails
#[instrument(skip(self))]
pub async fn delete(&self, id: impl fmt::Display + fmt::Debug) -> Result<()> {
self.http_client
.delete(format!("/v2/annotations/{id}"))
.await
}
}
27 changes: 27 additions & 0 deletions src/annotations/model.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use chrono::FixedOffset;
use serde::{Deserialize, Serialize};
use url::Url;

/// An annotation.
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
#[serde(rename_all = "camelCase")]

pub struct Annotation {
/// Unique ID of the annotation
pub id: String,
/// Type of the event marked by the annotation. Use only alphanumeric characters or hyphens. For example, "production-deployment".
#[serde(rename = "type")]
pub annotation_type: String,
/// Dataset names for which the annotation appears on charts
pub datasets: Vec<String>,
/// Explanation of the event the annotation marks on the charts
pub description: Option<String>,
/// Summary of the annotation that appears on the charts
pub title: Option<String>,
/// URL relevant for the event marked by the annotation. For example, link to GitHub pull request.
pub url: Option<Url>,
/// Time the annotation marks on the charts. If you don't include this field, Axiom assigns the time of the API request to the annotation.
pub time: chrono::DateTime<FixedOffset>,
///End time of the annotation
pub end_time: Option<chrono::DateTime<FixedOffset>>,
}
Loading
Loading