Skip to content

Commit

Permalink
Merge pull request #60 from axiomhq/annotations
Browse files Browse the repository at this point in the history
Add annitation support
  • Loading branch information
Licenser authored Jun 17, 2024
2 parents b9cf42c + fb4f183 commit 85f4ffd
Show file tree
Hide file tree
Showing 23 changed files with 1,207 additions and 414 deletions.
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
}

/// 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

0 comments on commit 85f4ffd

Please sign in to comment.