Restore Location fields (#5208)
* Add latitude/longitude fields to Location * Add regression test * Bump package version * Load secret during workflow
This commit is contained in:
@@ -34,6 +34,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
IPINFO_API_TOKEN: ${{ secrets.IPINFO_API_TOKEN }}
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
|
||||
|
||||
Generated
+1
-1
@@ -6062,7 +6062,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-api"
|
||||
version = "1.0.0-rc.5"
|
||||
version = "1.0.0-rc.6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.7",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-node-status-api"
|
||||
version = "1.0.0-rc.5"
|
||||
version = "1.0.0-rc.6"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use cosmwasm_std::{Addr, Coin};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
pub(crate) struct IpInfoClient {
|
||||
client: reqwest::Client,
|
||||
@@ -15,11 +15,7 @@ impl IpInfoClient {
|
||||
}
|
||||
|
||||
pub(crate) async fn locate_ip(&self, ip: impl AsRef<str>) -> anyhow::Result<Location> {
|
||||
let url = format!(
|
||||
"https://ipinfo.io/{}/country?token={}",
|
||||
ip.as_ref(),
|
||||
&self.token
|
||||
);
|
||||
let url = format!("https://ipinfo.io/{}?token={}", ip.as_ref(), &self.token);
|
||||
let response = self
|
||||
.client
|
||||
.get(url)
|
||||
@@ -33,11 +29,12 @@ impl IpInfoClient {
|
||||
}
|
||||
anyhow::Error::from(err)
|
||||
})?;
|
||||
let response_text = response.text().await?.trim().to_string();
|
||||
let raw_response = response.text().await?;
|
||||
let response: LocationResponse =
|
||||
serde_json::from_str(&raw_response).inspect_err(|e| tracing::error!("{e}"))?;
|
||||
let location = response.into();
|
||||
|
||||
Ok(Location {
|
||||
two_letter_iso_country_code: response_text,
|
||||
})
|
||||
Ok(location)
|
||||
}
|
||||
|
||||
/// check DOESN'T consume bandwidth allowance
|
||||
@@ -64,7 +61,7 @@ impl IpInfoClient {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub(crate) struct NodeGeoData {
|
||||
pub(crate) identity_key: String,
|
||||
pub(crate) owner: Addr,
|
||||
@@ -72,15 +69,55 @@ pub(crate) struct NodeGeoData {
|
||||
pub(crate) location: Location,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Default, Serialize)]
|
||||
pub(crate) struct Location {
|
||||
pub(crate) two_letter_iso_country_code: String,
|
||||
#[serde(flatten)]
|
||||
pub(crate) location: Coordinates,
|
||||
}
|
||||
|
||||
impl From<LocationResponse> for Location {
|
||||
fn from(value: LocationResponse) -> Self {
|
||||
Self {
|
||||
two_letter_iso_country_code: value.two_letter_iso_country_code,
|
||||
location: value.loc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub(crate) struct LocationResponse {
|
||||
#[serde(rename = "country")]
|
||||
pub(crate) two_letter_iso_country_code: String,
|
||||
#[serde(deserialize_with = "deserialize_loc")]
|
||||
pub(crate) loc: Coordinates,
|
||||
}
|
||||
|
||||
fn deserialize_loc<'de, D>(deserializer: D) -> Result<Coordinates, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let loc_raw = String::deserialize(deserializer)?;
|
||||
match loc_raw.split_once(',') {
|
||||
Some((lat, long)) => Ok(Coordinates {
|
||||
latitude: lat.parse().map_err(serde::de::Error::custom)?,
|
||||
longitude: long.parse().map_err(serde::de::Error::custom)?,
|
||||
}),
|
||||
None => Err(serde::de::Error::custom("coordinates")),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub(crate) struct Coordinates {
|
||||
pub(crate) latitude: f64,
|
||||
pub(crate) longitude: f64,
|
||||
}
|
||||
|
||||
impl Location {
|
||||
pub(crate) fn empty() -> Self {
|
||||
Self {
|
||||
two_letter_iso_country_code: String::new(),
|
||||
location: Coordinates::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,3 +147,38 @@ pub(crate) mod ipinfo {
|
||||
pub(crate) remaining: u64,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod api_regression {
|
||||
|
||||
use super::*;
|
||||
use std::{env::var, sync::LazyLock};
|
||||
|
||||
static IPINFO_TOKEN: LazyLock<String> = LazyLock::new(|| var("IPINFO_API_TOKEN").unwrap());
|
||||
|
||||
#[tokio::test]
|
||||
async fn should_parse_response() {
|
||||
let client = IpInfoClient::new(&(*IPINFO_TOKEN));
|
||||
let my_ip = reqwest::get("https://api.ipify.org")
|
||||
.await
|
||||
.expect("Couldn't get own IP")
|
||||
.text()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let location_result = client.locate_ip(my_ip).await;
|
||||
assert!(location_result.is_ok(), "Did ipinfo response change?");
|
||||
|
||||
assert!(
|
||||
client.check_remaining_bandwidth().await.is_ok(),
|
||||
"Failed to check remaining bandwidth?"
|
||||
);
|
||||
|
||||
// when serialized, these fields should be present because they're exposed over API
|
||||
let location_result = location_result.unwrap();
|
||||
let json = serde_json::to_value(&location_result).unwrap();
|
||||
assert!(json.get("two_letter_iso_country_code").is_some());
|
||||
assert!(json.get("latitude").is_some());
|
||||
assert!(json.get("longitude").is_some());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user