Skip to content

Commit

Permalink
auth and invalid json fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
jerbly committed Jan 13, 2024
1 parent c210d16 commit a6705cd
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 49 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "honey-explore"
version = "0.1.1"
version = "0.1.2"
edition = "2021"
authors = ["Jeremy Blythe <[email protected]>"]

Expand Down
149 changes: 105 additions & 44 deletions src/honeycomb.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use std::env;
use std::{
collections::HashMap,
env,
fmt::{Display, Formatter},
};

use anyhow::Context;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Debug, Clone)]
pub struct HoneyComb {
Expand All @@ -14,7 +19,7 @@ const HONEYCOMB_API_KEY: &str = "HONEYCOMB_API_KEY";
#[derive(Debug, Deserialize)]
pub struct Dataset {
pub slug: String,
pub last_written_at: DateTime<Utc>,
pub last_written_at: Option<DateTime<Utc>>,
}

#[derive(Debug, Deserialize, Serialize)]
Expand Down Expand Up @@ -42,6 +47,41 @@ struct Query {
id: String,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct NameAndSlug {
pub name: String,
pub slug: String,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Authorizations {
pub api_key_access: HashMap<String, bool>,
pub environment: NameAndSlug,
pub team: NameAndSlug,
}

impl Authorizations {
pub fn has_required_access(&self, access_types: &[&str]) -> bool {
access_types
.iter()
.all(|access_type| *self.api_key_access.get(*access_type).unwrap_or(&false))
}
}

impl Display for Authorizations {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut api_key_access = String::new();
for (key, value) in &self.api_key_access {
api_key_access.push_str(&format!("{}: {}\n", key, value));
}
write!(
f,
"api_key_access:\n{}\nenvironment: {}\nteam: {}",
api_key_access, self.environment.name, self.team.name
)
}
}

impl HoneyComb {
pub fn new() -> anyhow::Result<Self> {
Ok(Self {
Expand All @@ -51,70 +91,91 @@ impl HoneyComb {
))?,
})
}
pub async fn list_all_datasets(&self) -> anyhow::Result<Vec<Dataset>> {

async fn get<T>(&self, request: &str) -> anyhow::Result<T>
where
T: serde::de::DeserializeOwned,
{
let response = reqwest::Client::new()
.get(format!("{}datasets", URL))
.get(format!("{}{}", URL, request))
.header("X-Honeycomb-Team", &self.api_key)
.send()
.await?
.json::<Vec<Dataset>>()
.await?;
Ok(response)
let text: String = response.text().await?;

match serde_json::from_str::<T>(&text) {
Ok(t) => Ok(t),
Err(e) => {
eprintln!("Invalid JSON data: {}", text);
Err(anyhow::anyhow!("Failed to parse JSON data: {}", e))
}
}
}

pub async fn list_authorizations(&self) -> anyhow::Result<Authorizations> {
self.get("auth").await
}
pub async fn list_all_datasets(&self) -> anyhow::Result<Vec<Dataset>> {
self.get("datasets").await
}
pub async fn list_all_columns(&self, dataset_slug: &str) -> anyhow::Result<Vec<Column>> {
self.get(&format!("columns/{}", dataset_slug)).await
}

async fn post<T>(&self, request: &str, json: Value) -> anyhow::Result<T>
where
T: serde::de::DeserializeOwned,
{
let response = reqwest::Client::new()
.get(format!("{}columns/{}", URL, dataset_slug))
.post(format!("{}{}", URL, request))
.header("X-Honeycomb-Team", &self.api_key)
.json(&json)
.send()
.await?
.json::<Vec<Column>>()
.await?;
Ok(response)
let text: String = response.text().await?;

match serde_json::from_str::<T>(&text) {
Ok(t) => Ok(t),
Err(e) => {
eprintln!("Invalid JSON data: {}", text);
Err(anyhow::anyhow!("Failed to parse JSON data: {}", e))
}
}
}

pub async fn get_exists_query_url(
&self,
dataset_slug: &str,
column_id: &str,
) -> anyhow::Result<String> {
let response = reqwest::Client::new()
.post(format!("{}queries/{}", URL, dataset_slug))
.header("X-Honeycomb-Team", &self.api_key)
.json(&serde_json::json!({
"breakdowns": [
column_id
],
"calculations": [{
"op": "COUNT"
}],
"filters": [
{
let query: Query = self
.post(
&format!("queries/{}", dataset_slug),
serde_json::json!({
"breakdowns": [column_id],
"calculations": [{
"op": "COUNT"
}],
"filters": [{
"column": column_id,
"op": "exists",
}
],
"time_range": 7200
}))
.send()
.await?
.json::<Query>()
}],
"time_range": 7200
}),
)
.await?;

let query_id = response.id;

let response = reqwest::Client::new()
.post(format!("{}query_results/{}", URL, dataset_slug))
.header("X-Honeycomb-Team", &self.api_key)
.json(&serde_json::json!({
"query_id": query_id,
"disable_series": false,
"limit": 10000
}))
.send()
.await?
.json::<QueryResult>()
let query_result: QueryResult = self
.post(
&format!("query_results/{}", dataset_slug),
serde_json::json!({
"query_id": query.id,
"disable_series": false,
"limit": 10000
}),
)
.await?;

Ok(response.links.query_url)
Ok(query_result.links.query_url)
}
}
18 changes: 15 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,21 @@ async fn main() -> anyhow::Result<()> {

// fetch all the honeycomb data and build a map of attribute name to datasets
let hc = match HoneyComb::new() {
Ok(hc) => Some(hc),
Ok(hc) => {
let auth = hc.list_authorizations().await?;
let required_access = ["columns", "createDatasets", "queries"];
if auth.has_required_access(&required_access) {
Some(hc)
} else {
eprintln!(
"continuing without honeycomb: missing required access {:?}:\n{}",
required_access, auth
);
None
}
}
Err(e) => {
println!("continuing without honeycomb: {}", e);
eprintln!("continuing without honeycomb: {}", e);
None
}
};
Expand All @@ -116,7 +128,7 @@ async fn main() -> anyhow::Result<()> {
.await?
.iter()
.filter_map(|d| {
if (now - d.last_written_at).num_days() < 60 {
if (now - d.last_written_at.unwrap_or(now)).num_days() < 60 {
Some(d.slug.clone())
} else {
None
Expand Down

0 comments on commit a6705cd

Please sign in to comment.